llvm-mos
llvm_mos
https://llvm-mos.org/wiki/Welcome
MediaWiki 1.43.3
first-letter
Media
Special
Talk
User
User talk
llvm-mos
llvm-mos talk
File
File talk
MediaWiki
MediaWiki talk
Template
Template talk
Help
Help talk
Category
Category talk
Module
Module talk
Welcome
0
1
1
2021-03-08T02:23:15Z
MediaWiki default
2
wikitext
text/x-wiki
<strong>MediaWiki has been installed.</strong>
Consult the [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide] for information on using the wiki software.
== Getting started ==
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]
* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]
5702e4d5fd9173246331a889294caf01a3ad3706
2
1
2021-03-08T02:48:44Z
Jbyrd
1
Trivial edit.
wikitext
text/x-wiki
<strong>MediaWiki has been installed.</strong>
Consult the [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide] for information.
== Getting started ==
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]
* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]
afa20e333c5a6c73f4ad94c39a26e47b6e8bd0cf
3
2
2021-03-08T02:51:58Z
Jbyrd
1
/* Getting started */
wikitext
text/x-wiki
Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
df9ea1918103450a6cb3f661dcadefba0f111b3d
6
3
2021-03-13T07:56:10Z
Jbyrd
1
Jbyrd moved page [[Main Page]] to [[llvm-mos:Main Page]]
wikitext
text/x-wiki
Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
df9ea1918103450a6cb3f661dcadefba0f111b3d
9
6
2021-03-13T08:01:07Z
Jbyrd
1
Jbyrd moved page [[llvm-mos:Main Page]] to [[llvm-mos:Welcome to llvm-mos]]
wikitext
text/x-wiki
Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
df9ea1918103450a6cb3f661dcadefba0f111b3d
11
9
2021-03-13T08:01:28Z
Jbyrd
1
Jbyrd moved page [[llvm-mos:Welcome to llvm-mos]] to [[Welcome to llvm-mos]]
wikitext
text/x-wiki
Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
df9ea1918103450a6cb3f661dcadefba0f111b3d
13
11
2021-03-13T08:02:14Z
Jbyrd
1
Jbyrd moved page [[Welcome to llvm-mos]] to [[Welcome]]
wikitext
text/x-wiki
Welcome to llvm-mos! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
df9ea1918103450a6cb3f661dcadefba0f111b3d
16
13
2021-03-13T08:05:18Z
Jbyrd
1
wikitext
text/x-wiki
Welcome to the llvm-mos project! This is an experiment to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
79d3dbfeaea91f7edfd31ebb4ab94b2b9e709502
18
16
2021-03-13T09:40:57Z
Jbyrd
1
wikitext
text/x-wiki
Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
Although this project is in its infancy, it's still able to compile and link simple programs for several popular microcomputers of the 1980s.
85af5e4e71f51d616f3da312a2bb4fbcf25e5eee
19
18
2021-03-14T18:12:56Z
Jbyrd
1
wikitext
text/x-wiki
Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s.
11cd53cbab46bd107faab6f97ac0df30fc573a45
21
19
2021-03-14T18:40:49Z
Jbyrd
1
Add overview
wikitext
text/x-wiki
Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s.
* [[Overview]]
88fa22569f957946d919e0efaeab8f1dda35f3f9
23
21
2021-03-14T19:13:08Z
Jbyrd
1
Add C64 image
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s.
* [[Overview]]
37a01151fc2306510320f5871a6deb94bc867c6a
25
23
2021-03-14T19:14:56Z
Jbyrd
1
Add VIC-20 image
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s.
* [[Overview]]
95da4e0a1993486425d2e44b4c77db6d0cbf7b10
27
25
2021-03-14T19:15:54Z
Jbyrd
1
Add Apple IIe
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project! This is an experimental project to provide first-class support in LLVM for the MOS 6502 family of microprocessors.
This includes a modern C compiler, assembler, and linker, with example code targeting several popular targets.
Although this project is early, it's still able to compile and link simple programs for several popular microcomputers of the 1980s.
* [[Overview]]
2e74272267b865f379677427a356d478b5b1f1f8
28
27
2021-03-14T19:27:08Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, it is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. We welcome your assistance and contributions to this project.
94cafb2082154daafc68a3371fcc0015a6195131
29
28
2021-03-14T19:27:43Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace]. We welcome your assistance and contributions to this project.
b4da84cdef3ab1b98971c12aa023ac96f0f51b5d
30
29
2021-03-14T19:49:07Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile short programs for the 6502 series. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace].
If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
a3bee2cb95e1e2cfba672a7c0ed210bb1012ec99
31
30
2021-03-14T19:50:08Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace].
If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
2bc2e3cfcbc5dbc7fc4650abf005722e257b6f72
32
31
2021-03-14T19:51:55Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur on [Slack, in the https://llvm-mos.slack.com/ llvm-mos workspace].
If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
c36c2498c535a701a4a18bc63953c63b800af5b0
33
32
2021-03-14T19:52:34Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for a generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
98925b908dd5c288bda25035c2b316f9fb4ab6bd
34
33
2021-03-14T19:53:04Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many abortive attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
67a2e54e61b45b727a87aef297eab0a54e246d9c
35
34
2021-03-14T19:53:28Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
Welcome to the llvm-mos project!
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
7997f30e2c712db3e0ec33e72de436535ec0b526
36
35
2021-03-14T19:56:22Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
0eb36125387da5cdc3fb25936c17a02f3e919736
40
36
2021-03-14T20:09:29Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
== Key topics ==
* [[Assembler]]
*
6610c05db116494d1b82a06733a4106209635003
MediaWiki:Sidebar
8
2
4
2021-03-08T19:23:17Z
Jbyrd
1
Remove items from toolbar
wikitext
text/x-wiki
* navigation
** mainpage|mainpage-description
** recentchanges-url|recentchanges
* SEARCH
* TOOLBOX
* LANGUAGES
cf3659e2214a1a0b03d425827bccf346d62ca332
MediaWiki:Mainpage
8
3
5
2021-03-13T07:55:26Z
Jbyrd
1
Created page with "llvm-mos"
wikitext
text/x-wiki
llvm-mos
d584aef15e3d09e9e3b3a0dc9273a4a920b98d5c
8
5
2021-03-13T08:00:11Z
Jbyrd
1
wikitext
text/x-wiki
Welcome to llvm-mos
8522d8dc1e6e7a0dd2d7fa610089754121d7a073
15
8
2021-03-13T08:02:45Z
Jbyrd
1
wikitext
text/x-wiki
Welcome
ca4f9dcf204e2037bfe5884867bead98bd9cbaf8
Main Page
0
4
7
2021-03-13T07:56:10Z
Jbyrd
1
Jbyrd moved page [[Main Page]] to [[llvm-mos:Main Page]]
wikitext
text/x-wiki
#REDIRECT [[llvm-mos:Main Page]]
789dd409bf14e59df1730b74d34d90dc38d078c2
llvm-mos:Main Page
4
5
10
2021-03-13T08:01:07Z
Jbyrd
1
Jbyrd moved page [[llvm-mos:Main Page]] to [[llvm-mos:Welcome to llvm-mos]]
wikitext
text/x-wiki
#REDIRECT [[llvm-mos:Welcome to llvm-mos]]
3efc41d79129eadb73d65ba1407405b3781eb47b
llvm-mos:Welcome to llvm-mos
4
6
12
2021-03-13T08:01:28Z
Jbyrd
1
Jbyrd moved page [[llvm-mos:Welcome to llvm-mos]] to [[Welcome to llvm-mos]]
wikitext
text/x-wiki
#REDIRECT [[Welcome to llvm-mos]]
95cdae98026c30eb20acb9a95880eb3f6e2b0b44
Overview
0
9
20
2021-03-14T18:39:40Z
Jbyrd
1
Created page with "The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [https://en.wikipe..."
wikitext
text/x-wiki
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [https://en.wikipedia.org/wiki/MOS_Technology MOS Technology] 65xx series of microprocessors and their clones.
This will permit modern software and development methods to target common microcomputers of the 1980s, including but not limited to the [https://en.wikipedia.org/wiki/Commodore_64 Commodore 64], the [https://en.wikipedia.org/wiki/Apple_IIe Apple IIe], and the [https://en.wikipedia.org/wiki/Atari_8-bit_family Atari 8-bit family].
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing development discussions occur on Slack, in the [https://llvm-mos.slack.com/ llvm-mos workspace].
8c894ec7af71271f84b3b5c6dadca06833f627cf
File:Hello-c64.png
6
10
22
2021-03-14T19:04:21Z
Jbyrd
1
wikitext
text/x-wiki
Initial bringup of assembler on C64 emulator
5a8f43802ce717562a5ba060b181430a487d2b6d
File:Hello-vic20.png
6
11
24
2021-03-14T19:14:10Z
Jbyrd
1
wikitext
text/x-wiki
Hello world of LLVM assembler targeting Commodore VIC-20
7f210be090e797335245af6a7dbf1395c670d738
File:Hello-apple2.png
6
12
26
2021-03-14T19:15:22Z
Jbyrd
1
wikitext
text/x-wiki
Hello world targeting Apple IIe
d07f6f8a4816d49f19695f126e7e024ac91803ee
Assembler
0
13
37
2021-03-14T20:02:30Z
Jbyrd
1
Created page with "The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer ma..."
wikitext
text/x-wiki
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos`) as a parameter to any tool.
47af71b392f8b9595f7f8026f259c55766d06040
38
37
2021-03-14T20:06:24Z
Jbyrd
1
wikitext
text/x-wiki
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
6aa5f15b77237e411d07d14e5764bea642e829cf
39
38
2021-03-14T20:07:22Z
Jbyrd
1
wikitext
text/x-wiki
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
7ed2ec136917853a29d72a6d4b86d9b2960acd55
ELF overview
0
14
41
2021-03-14T20:27:56Z
Jbyrd
1
Created page with "Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to p..."
wikitext
text/x-wiki
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump basic information about MOS executables.
a4284960ab7bab36eb904d7baa66506da9f5ed2c
42
41
2021-03-14T20:29:05Z
Jbyrd
1
wikitext
text/x-wiki
Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected.
This also means that generic tools that work on ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump basic information about MOS executables.
4a3fa7eeabd2dc2d5d8b316144fb1b5415aa2fc5
43
42
2021-03-14T20:31:17Z
Jbyrd
1
wikitext
text/x-wiki
Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected.
Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that work on ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries.
6a440be632bbfe1cd637ff7c404dbb35c3e50d95
44
43
2021-03-14T20:32:00Z
Jbyrd
1
wikitext
text/x-wiki
Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected.
Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries.
49b259fcaecf8a393aea6ff03fa5692f49b6db8b
45
44
2021-03-14T20:34:13Z
Jbyrd
1
Jbyrd moved page [[ELF]] to [[ELF overview]] without leaving a redirect
wikitext
text/x-wiki
Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected.
Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries.
49b259fcaecf8a393aea6ff03fa5692f49b6db8b
ELF specification
0
15
46
2021-03-14T20:37:27Z
Jbyrd
1
Created page with "This is version 0.1.2 (DRAFT) of this specification. == Overview == This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of pro..."
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
| Name | Value | Description |
| --- | --- | --- |
| EM_MOS_6502 | 0x00000001 | MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [MCS6500 Microcomputer Family Programming Manual, Second Edition](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf). This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |
| EM_MOS_6502_BCD | 0x00000002 | MOS 6502 compatible [BCD (binary coded decimal) instruction handling](http://www.6502.org/tutorials/decimal_mode.html), including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [Ricoh 2A0x](https://en.wikipedia.org/wiki/Ricoh_2A03), have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
| EM_MOS_6502X | 0x00000004 | NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [6502/6510/8500/8502 Opcode matrix](http://www.oxyron.de/html/opcodes02.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |
| EM_MOS_65C02 | 0x00000008 | MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |
| EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |
| EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
| EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |
| EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
| EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt)
[MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf)
[Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf)
17cf049a560bad27aa6a4bf39df893dc8300549b
47
46
2021-03-14T20:40:41Z
Jbyrd
1
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [MCS6500 Microcomputer Family Programming Manual, Second Edition](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf). This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm. |
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [BCD (binary coded decimal) instruction handling](http://www.6502.org/tutorials/decimal_mode.html), including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [Ricoh 2A0x](https://en.wikipedia.org/wiki/Ricoh_2A03), have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X | 0x00000004 | NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [6502/6510/8500/8502 Opcode matrix](http://www.oxyron.de/html/opcodes02.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |
|-
|-
| EM_MOS_65C02 | 0x00000008 | MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |
|-
| EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |
|-
| EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |
|-
| EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|}
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt)
[MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf)
[Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf)
2a770872adb689d2c292df19a52a796e72c956e7
48
47
2021-03-14T20:41:26Z
Jbyrd
1
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [MCS6500 Microcomputer Family Programming Manual, Second Edition](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf). This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [BCD (binary coded decimal) instruction handling](http://www.6502.org/tutorials/decimal_mode.html), including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [Ricoh 2A0x](https://en.wikipedia.org/wiki/Ricoh_2A03), have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [6502/6510/8500/8502 Opcode matrix](http://www.oxyron.de/html/opcodes02.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|-
| EM_MOS_65C02 | 0x00000008 | MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |
|-
| EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |
|-
| EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |
|-
| EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|}
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt)
[MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf)
[Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf)
6117129570f3c8dc2f37d5a492e82b61b94793ab
49
48
2021-03-14T20:45:18Z
Jbyrd
1
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 bit is set to one. |
|-
| EM_MOS_R65C02 | 0x00000010 | MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |
|-
| EM_MOS_W65C02 | 0x00000020 | MOS 65C02 WDC only instructions, including STP and WAI, as defined in [65C02 Opcodes](http://6502.org/tutorials/65c02opcodes.html). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_W65816 | 0x0000100 | MOS 65C816 native mode instructions and addressing modes, as defined in [Programming the 65816, Including the 6502, 65C02 and 65802](http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf) and [65C816 Opcodes](http://6502.org/tutorials/65c816opcodes.html). This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02. |
|-
| EM_MOS_65EL02 | 0x00000200 | 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_65CE02 | 0x00000400 | 65CE02 only instructions, as defined in [64tass reference manual](http://tass64.sourceforge.net/#opcodes-65el02). This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|}
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt)
[MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf)
[Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf)
aba37eb97c6fa281d45031710e82f3bb03558572
50
49
2021-03-14T21:11:00Z
Jbyrd
1
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one. |
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one. |
|-
| EM_MOS_W65816 || 0x0000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt)
[MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf)
[Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf)
c01016396c3f6e52f3fdb1d834733a1aeff4a84f
51
50
2021-03-14T21:13:33Z
Jbyrd
1
/* Machine type field */
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x0000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[Commodore Semiconductor Group CSG65CE02 Technical Reference](https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt)
[MCS6500 Family Microcomputer Programming Manual, January 1976](http://archive.6502.org/books/mcs6500_family_programming_manual.pdf)
[Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2](https://refspecs.linuxfoundation.org/elf/elf.pdf)
6b147d126e6d69e92ed15a7815046856a052e832
52
51
2021-03-14T21:15:11Z
Jbyrd
1
wikitext
text/x-wiki
This is version 0.1.2 (DRAFT) of this specification.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ Caption text
|-
! Header text !! Header text !! Header text
|-
| Example || Example || Example
|-
| Example || Example || Example
|-
| Example || Example || Example
|}
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
# Section flags
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
| Name | Value | Description |
| --- | --- | --- |
| SHF_MOS_ZEROPAGE | 0x10000000 | This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
# Relocation types
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
# References
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]()
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
f90d1f5f4a0c9e1776eb82c6ee3d6a6593775afb
ELF specification
0
15
53
52
2021-03-14T21:18:23Z
Jbyrd
1
wikitext
text/x-wiki
- ELF specification for MOS-compatible processors -
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [MOS.def](../blob/master/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def).
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]()
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
3f5f773305c7fc387f3bc37fb75aa5572e3ebb06
54
53
2021-03-14T21:22:31Z
Jbyrd
1
/* Relocation types */
wikitext
text/x-wiki
- ELF specification for MOS-compatible processors -
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def<nowiki> MOS.def].</nowiki>
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]()
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
f6eb697cd7930b2f6bf5769cbc304e7a7e4dde33
55
54
2021-03-14T21:22:49Z
Jbyrd
1
wikitext
text/x-wiki
- ELF specification for MOS-compatible processors -
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def].</nowiki>
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]()
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
b4a344911c904c4ef329d2f6e20a1f4656f8a603
56
55
2021-03-14T21:24:06Z
Jbyrd
1
wikitext
text/x-wiki
- ELF specification for MOS-compatible processors -
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def].
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]()
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
7eca7f2f28284e98570ae1976e04fbda5aefbeeb
57
56
2021-03-14T21:24:27Z
Jbyrd
1
wikitext
text/x-wiki
- ELF specification for MOS-compatible processors -
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]()
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
f11c3a81745cbfa8f2b7b1151f2832f3497d9402
58
57
2021-03-14T21:24:56Z
Jbyrd
1
wikitext
text/x-wiki
- ELF specification for MOS-compatible processors -
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
== Overview ==
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
ab09e625ec0ea5328b47b2ee0ba7c4275478c926
59
58
2021-03-14T21:25:40Z
Jbyrd
1
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
== Section flags ==
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
== Relocation types ==
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
8698e91e873771ecd9263482809695e3f06a452c
60
59
2021-03-14T21:26:44Z
Jbyrd
1
/* Section flags */
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit may be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
927754240ca05b28154519af54e27d6d58ba45d3
ELF overview
0
14
61
45
2021-03-14T21:33:39Z
Jbyrd
1
wikitext
text/x-wiki
Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected.
Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries.
There exists a more precise [[ELF specification]] for llvm-mos.
071e618f84a795698b50f6ddfb80e4739f9daeb2
Welcome
0
1
62
40
2021-03-14T21:34:43Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
== Key topics ==
* [[Assembler]]
* Executable and linker format
** [[ELF overview]]
** [[ELF specification]]
*
1538e8fa69b268f7da944f2b537aadf719d84b21
69
62
2021-03-15T08:47:26Z
Jbyrd
1
Add Modifiers
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
== Key topics ==
* Assembly
** [[Assembler]]
** Executable and linker format
***[[ELF overview]]
***[[ELF specification]]
** [[Modifiers]]
* C compiler [tbd]
*
7b2cd6e0adf765742ee3b9b94e80b62ef10349ce
70
69
2021-03-15T08:55:51Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
** Executable and linker format
***
**[[Modifiers]]
* C
* Linking
** [[ELF overview]]
**[[ELF specification]]
*
1aac117ecd2f6420d33c90fad9abc73ea0df644e
71
70
2021-03-15T08:56:25Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
* Linking
** [[ELF overview]]
**[[ELF specification]]
*
73454e1a7ea615faec2abd74feb19f6ee618538e
73
71
2021-03-15T10:06:30Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
** [[C calling convention]]
* Linking
** [[ELF overview]]
**[[ELF specification]]
*
24fd2afe6697312e610bc18f104bcf35eb9b4a33
77
73
2021-03-15T10:22:40Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
**Zero page
* C
** C compiler
**[[C calling convention]]
** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[https://lld.llvm.org/ lld overview]
*
c57423bdfdd5beb58d9bb421bdfbe50ace3d6742
78
77
2021-03-15T10:25:48Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
** [[C compiler]]
** [[C calling convention]]
** clang general documentation
* Linking
** [[ELF overview]]
**[[ELF specification]]
*
3e3601218f7b1dcab3937c4bd80e673ca266d315
83
78
2021-03-15T10:36:02Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
** [[C compiler]]
** [[C calling convention]]
** clang general documentation
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
ad21e477c68ba2830d02f608c583fa3384f4c0d1
84
83
2021-03-15T10:36:47Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help us out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
b5a4e0af0735b69a52686398902dd7bf49086ea8
85
84
2021-03-15T10:48:55Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The purpose of the llvm-mos project is to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
ed939f2ebbaaee67892d0a325190272d7feb847e
86
85
2021-03-15T10:54:03Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
90dc03c9867b796b64787530f479622734c3bbf4
87
86
2021-03-15T10:56:24Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
In parallel with the main LLVM tool chain, we are developing the [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos SDK], which demonstrates practical use of llvm-mos to target specific 6502 machines and emulators.
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
d9817393eea56eb3a93973c4ad0eb48603d998c7
97
87
2021-03-15T22:11:50Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
7a673d8dd69d787d84390774260f58cde9949be0
99
97
2021-04-18T23:40:08Z
Jbyrd
1
Added link to debugging codegen
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [[Debugging code generation]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
278af6833c2bfce83ecfd9ab15f278e28eb2a9bb
102
99
2021-05-05T18:27:57Z
2603:8000:3F01:9052:A9A3:FE64:6BAE:2265
0
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [[Debugging code generation]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
* [[Emulator]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
28615e000181a4f7a393c234bd143bc922e4be2a
Modifiers
0
16
63
2021-03-15T02:04:56Z
Jbyrd
1
Created page with "When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or..."
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA [the lowest byte in the 16-bit address of QSORT]
However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well.
In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the `QSORT` address is loaded into the accumulator.
The following modifiers are available for your use:
| Name | Description |
| --- | --- |
| mos16lo() or < | The lowest byte in the given 16-bit address. |
| mos16hi() or > | The highest byte in the given 16-bit address. |
| mos24bank() | The 8-bit bank portion of the given 24-bit address. |
| mos24segment() | The segment 16-bit portion of the given 24-bit address. |
| mos24segmentlo() | The lowest byte in the segment of the given 24-bit address. |
| mos24segmenthi() | The highest byte in the segment of the given 24-bit address. |
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
6ec33a443544a7d5ad1adc0dd516c7c70b991982
64
63
2021-03-15T02:10:21Z
Jbyrd
1
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA [the lowest byte in the 16-bit address of QSORT]
However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well.
In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the `QSORT` address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Name !! Value !! Description
|-
| mos16lo() or < || The lowest byte in the given 16-bit address.
|-
| mos16hi() or > || The highest byte in the given 16-bit address.
| mos24bank() | The 8-bit bank portion of the given 24-bit address. |
| mos24segment() | The segment 16-bit portion of the given 24-bit address. |
| mos24segmentlo() | The lowest byte in the segment of the given 24-bit address. |
| mos24segmenthi() | The highest byte in the segment of the given 24-bit address. |
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
b75ab3e1543779874cde689c0921778e719d6c17
65
64
2021-03-15T02:12:55Z
104.189.8.152
0
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA [the lowest byte in the 16-bit address of QSORT]
However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well.
In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the `QSORT` address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| mos16lo() or < || The lowest byte in the given 16-bit address.
|-
| mos16hi() or > || The highest byte in the given 16-bit address.
|-
| mos24bank() | The 8-bit bank portion of the given 24-bit address. |
| mos24segment() | The segment 16-bit portion of the given 24-bit address. |
| mos24segmentlo() | The lowest byte in the segment of the given 24-bit address. |
| mos24segmenthi() | The highest byte in the segment of the given 24-bit address. |
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
d5d06441218211102807498cb53c941e42ca63f4
66
65
2021-03-15T02:13:35Z
104.189.8.152
0
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA [the lowest byte in the 16-bit address of QSORT]
However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well.
In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the `QSORT` address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| mos16lo() or < || The lowest byte in the given 16-bit address.
|-
| mos16hi() or > || The highest byte in the given 16-bit address.
|-
| mos24bank() || The 8-bit bank portion of the given 24-bit address.
|-
| mos24segment() || The segment 16-bit portion of the given 24-bit address.
|-
| mos24segmentlo() || The lowest byte in the segment of the given 24-bit address.
|-
| mos24segmenthi() || The highest byte in the segment of the given 24-bit address.
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
bf24f1a91a0eb5a966996c89c119935b00e20953
67
66
2021-03-15T02:13:51Z
104.189.8.152
0
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA [the lowest byte in the 16-bit address of QSORT]
However, the address of QSORT may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of QSORT won't be known until then as well.
In LLVM land, that operator is referred to as a **modifier**. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If `QSORT` refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the `QSORT` address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| mos16lo() or < || The lowest byte in the given 16-bit address.
|-
| mos16hi() or > || The highest byte in the given 16-bit address.
|-
| mos24bank() || The 8-bit bank portion of the given 24-bit address.
|-
| mos24segment() || The segment 16-bit portion of the given 24-bit address.
|-
| mos24segmentlo() || The lowest byte in the segment of the given 24-bit address.
|-
| mos24segmenthi() || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, mos24segment() returns a 16-bit address, so you can't do `LDA #mos24segment(QSORT)`, because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator **<** and the greater-than operator **>** as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
6b13699b7e99bb98e89180eecdfc1bea4d32572a
68
67
2021-03-15T08:46:22Z
Jbyrd
1
Formatting cleanup
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
0fe2ae61ef89719b495708167994ddc222830b93
C calling convention
0
17
72
2021-03-15T10:01:09Z
Jbyrd
1
Created page with "The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning: * Aggregate types (structs, arrays, etc.) are..."
wikitext
text/x-wiki
The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning:
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. It's not use having more than two, since at most 4 bytes of hard stack is allowed per frame, and spilling zero page registers to the soft stack isn't worthwhile.
* - Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
* - For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
95f70e6b4a59d3f1014590d9fedbeaee53d6f361
C compiler
0
18
74
2021-03-15T10:16:46Z
Jbyrd
1
Created page with "A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag --target=mos to clang. Like all LLVM backends, the bulk..."
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag --target=mos to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle lowering to a degree unusual for a processor of MOS's size.
fb7215a137bd9404233fe6a4a18b72e3b6a7becf
75
74
2021-03-15T10:17:56Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's tiny transistor count.
3126bd1454d457156a7a160bde10175d70f0c05c
76
75
2021-03-15T10:18:19Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
6206d2e37b94abe855b3cb85733489aec996b117
88
76
2021-03-15T11:06:36Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called zero page, which could be accessed relatively quickly and cheaply. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', to diffentiate from LLVM's virtual registers.
7d214c0f95720c7a143333cd1b55be34cc27a8d1
89
88
2021-03-15T11:07:07Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', to diffentiate from LLVM's virtual registers.
9ab8701c0905d0e29d810791b3b7ddae272d5769
90
89
2021-03-15T11:08:23Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning.
73aad752c7f5b4c8f8aecfe3c0cf72c84875c571
91
90
2021-03-15T11:09:04Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502, compensated for the 6502's small number of registers, by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning.
56486cbd47c23cde40e4cb56eafdf875cfcbf496
92
91
2021-03-15T11:09:18Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502, compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning.
3a3fb2fcad85a4a44665e4370794b77b18adddb1
93
92
2021-03-15T11:09:31Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning.
e0d5c664d99875883283316f4412a02380f21440
94
93
2021-03-15T11:10:02Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', as distinct from [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers], which has a different meaning.
dd8097310e3f8d8a80e69d78e9a2722d069f4422
95
94
2021-03-15T11:10:41Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', not to be confused with [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers].
39f32533754647b908bc32f2ab4c10bac4ac0860
96
95
2021-03-15T11:14:03Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . However, unlike most other LLVM backends, llvm-mos uses LLVM's new GlobalISel architecture for lowering LLVM-IR generic instructions to LLVM specific ones. It lowering IR instructions to MOS via a set of carefully chosen pseudoinstructions, which in turn represent logical groups of MOS instructions.
The llvm-mos backend performs almost no optimization of MOS code at the machine instruction level. Instead, LLVM is permitted to run multiple passes on MOS pseudoinstructions, which are lowered relatively late in the compilation pass pipeline to actual MOS instructions. Interestingly, this permits LLVM's existing core to handle optimization and lowering to a degree unusual for a processor of MOS's small size.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', which not to be confused with [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers]. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. However, the imaginary register locations are treated as symbols, which are not resolved to exact locations until link time.
df6add2c88007745fca42e3bc4edbc8baaa5850e
Assembler relaxation
0
19
79
2021-03-15T10:26:05Z
Jbyrd
1
Created page with "The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider: lda hel..."
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as 0xb5, indicating that hello is an 8-bit (zero page) address. Alternately, it might be encoded as 0xbd, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the hello symbol, or (2) receive a hint as to which address space that hello should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
- the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
- the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
- the symbol is defined in a section marked with the special z flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section zp
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the adrlowmemory symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the low_short symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the lda instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
f8316d67cf5cac5914d1b12771cab8300b12a3aa
80
79
2021-03-15T10:27:55Z
Jbyrd
1
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
<code>.section zplow_short:.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x</code>
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the adrlowmemory symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the low_short symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the lda instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
6bc03dc3a4f0077a7c1da0c3554c87220fd4e7d3
81
80
2021-03-15T10:28:47Z
Jbyrd
1
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section zp
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x</code>
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the adrlowmemory symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the low_short symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the lda instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
6ef7bcd48afe183ab921f3905557b4e665c235a3
82
81
2021-03-15T10:29:30Z
Jbyrd
1
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section zp
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x</code>
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
c2871207f364a0e425504f4b177c94247df44e8a
Debugging code generation
0
20
98
2021-04-18T23:39:04Z
Jbyrd
1
Initial authoring
wikitext
text/x-wiki
The llvm-mos code generator runs mostly on test cases written in MIR. These can be found in <code>llvm/test/CodeGen/MOS</code> .
To test a particular section of codegen, you typically compile the code with clang, and then use llc to watch as the GlobalISel architecture lowers the code from generic MIR to MOS code.
One way of doing this is by debugging program:
llc -O2 -debug --debug-pass=Details -verify-machineinstrs -o %t %s
Where %t is the assembly output file, and %s is the name of a LLVM bitcode file with an .ll extension.
This command line runs all the GlobalISel passes, emitting copious debug information on every pass.
b8f7f59d7afe604b0cc906cf617d106407521e2c
100
98
2021-04-21T20:49:41Z
Jbyrd
1
wikitext
text/x-wiki
The llvm-mos code generator runs mostly on test cases written in MIR. These can be found in <code>llvm/test/CodeGen/MOS</code> .
To test a particular section of codegen, you typically compile the code with clang, and then use llc to watch as the GlobalISel architecture lowers the code from generic MIR to MOS code.
One way of doing this is by debugging program:
llc -O2 -debug --debug-pass=Details -verify-machineinstrs -o %t %s
Where %t is the assembly output file, and %s is the name of a LLVM bitcode file with an .ll extension.
This command line runs all the GlobalISel passes, emitting copious debug information on every pass.
A formula for creating C code to test codegen passes is as follows:
clang --target=mos -emit-llvm -S %s -o %t.ll
llc -debug --debug-pass=Details -mtriple=mos -verify-machineinstrs %t.ll -o %t.s
2c6e74e1b0a379d32d10e01b2ae22a8535e9e390
Emulator overview
0
21
101
2021-04-30T21:57:53Z
Jbyrd
1
Created page with "As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well..."
wikitext
text/x-wiki
As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well as test suite verification, without requiring actual hardware to be present.
The intention of the emulator design is to permit multiple future iterations of emulation, without throwing away any existing functionality. Although a classic read-parse-execute style emulator would be sufficient for small tests, we'd want to be able to transpile generator code to the local machine's native instruction set, for maximum performance. We'd like to leave the door open to debugging with lldb or other tools, including reverse execution. And although all known LLVM targets have register sets and contiguous memory, we don't want to necessarily require any particular emulation to have these features.
About 80% of the functionality required to build an emulator, is already present within LLVM for most targets. LLVM already includes a disassembler class, MCDisassembler, which is implemented for most platforms. It's able to reconstruct a series of MCInst objects given a raw instruction stream. So, what's needed is a way to take the "current" MCInst, and emulate the behavior of it.
It's also natural to describe the behavior of each instruction, in the tablegen file that defines it. It allows similar instructions to be grouped and to share related code for related activities.
Since LLVM's tablegen already has complete knowledge of all the instructions present for the target, it makes sense to explain the emulator behavior of these instructions to tablegen, and then let tablegen collate all these behaviors into an .inc file that can be included within a larger emulator.
In keeping with the LLVM philosophy, MCEmulator is a set of building blocks for building emulators. It includes a command-line tool, llvm-emu, which demonstrates one way to combine those blocks into an emulator.
2942e7c8cb674e23ed075ec468c16d7598b71ca1
C calling convention
0
17
103
72
2021-05-05T18:33:21Z
Jbyrd
1
wikitext
text/x-wiki
The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning:
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. It's not use having more than two, since at most 4 bytes of hard stack is allowed per frame, and spilling zero page registers to the soft stack isn't worthwhile.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
* For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
87b53b75161578b442efda1b9da70b333c359dfa
104
103
2021-05-05T18:33:44Z
Jbyrd
1
wikitext
text/x-wiki
The C calling convention is mostly described inline within the source code. There are a few additional points worth mentioning:
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available. It's not use having more than two, since at most 4 bytes of hard stack is allowed per frame, and spilling zero page registers to the soft stack isn't worthwhile.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
53180336e6da6fcc6b1540e60317eeda2a6b8b7f
110
104
2021-05-27T05:26:14Z
Mysterymath
3
Provide full details of calling convention.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each imaginary (zero page) register, increasing.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers. If none are available, the bytes are split and passed as above.
* If no registers are available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
dadcc0d7a17658abd0a80f5f18362091a076be3e
Code generation overview
0
22
105
2021-05-15T20:48:37Z
Jbyrd
1
Created page with "The LLVM GlobalISel architecture allows for adding multiple sequential passes, each of which does exactly one thing, for reducing [https://llvm.org/docs/GlobalISel/GMIR.html g..."
wikitext
text/x-wiki
The LLVM GlobalISel architecture allows for adding multiple sequential passes, each of which does exactly one thing, for reducing [https://llvm.org/docs/GlobalISel/GMIR.html generic MIR] to 6502 code. Our current strategy is not to lower directly to 6502 code, but rather to lower MIR to a set of logical 6502-like opcodes. LLVM likes to operate on machines with plenty of registers, and instructions for which those registers can be changed. For example, instead of explaining the TYA, TAY, TXA, and TAX instructions to the mid-level pipeline, we create logical T_A and TA_ instructions, each of which accepts a register parameter which codegen can choose. These logical instructions are lowered much later in the pipeline to actual 6502 opcodes. This permits code generation to perceive the 6502 instruction set as a set of [opcode, register] pairs.
c0dcc88de4f74865ba4e6817f6f9f99ad368e33b
112
105
2021-05-27T05:41:08Z
Mysterymath
3
Move text from C compiler; that page should really talk more about the Clang side of things.
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from it's machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, which not to be confused with LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
59c6e7b1cacef41e25b58d6621a3d71789cd4e34
114
112
2021-06-01T21:23:38Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from it's machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, which not to be confused with LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
2e29c3ec5f6141e3710224ebf3ef718842103763
115
114
2021-06-01T21:24:05Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, which not to be confused with LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
bfe59cd60c9dc9eb47f05fb6a93d8e6b6688ff32
146
115
2021-06-02T07:58:54Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
f4b49189a1251859b6f5663aa57cf4d5db0df174
147
146
2021-06-02T07:59:17Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs, and it's not possible unless this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
30b6f7c8a4612bc32f7a9676675727c56c8ec54d
148
147
2021-06-02T08:00:07Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
f33c3f13bfcbce5161c0336a713fd5e758f74559
Welcome
0
1
106
102
2021-05-15T20:49:06Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C compiler]]
** [[C calling convention]]
** [[Debugging code generation]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
**[[Code generation]]
* [[Emulator]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
58f9257a786f1077b0c708547a9333cb1e72dc24
107
106
2021-05-15T20:50:03Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is an experiment to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
87f5a734cb21160456c5d2b976610fab187f8c12
108
107
2021-05-27T05:16:43Z
Mysterymath
3
MInor fixes to wording on welcome page.
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
**[[Zero page]]
*
06d02d95a646da7f583601e387002e3ec6060d09
124
108
2021-06-01T22:10:54Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*
5686ff1549c741d5926c03df2546532701ecb639
125
124
2021-06-01T22:15:41Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*
2815c72d1ff615beac80b8c87cfe8647ec1d036a
126
125
2021-06-02T04:17:48Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*
452fc5117f4525fed5f5f306407e8a55e005bacc
130
126
2021-06-02T04:24:39Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well. We provide builds of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[SDK]]
*
9b72ab1de286316c2b83db025982098b8e189d67
132
130
2021-06-02T04:27:17Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is creating a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[SDK]]
*
0f48e4e431b5f011e7efad9fd61442d1b81416ae
133
132
2021-06-02T04:52:15Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[SDK]]
*
c0065eca6d692681a41fc81df57840f5d0428cbf
134
133
2021-06-02T05:18:42Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[SDK]]
*
a30fdfeb22484760940c63b7045db06cf4491692
137
134
2021-06-02T05:39:19Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
6522d40213e4db6d7252bb8de62eba4306736f38
149
137
2021-06-02T09:44:36Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
ea237ad696e913809bb3b9574c7c08c785c7e4eb
151
149
2021-06-02T09:46:29Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
1d1c2807e6c17804ab68edfd2dd19bc34ec79a37
Modifiers
0
16
109
68
2021-05-27T05:18:40Z
Mysterymath
3
Add mos8 modifier.
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
21c85875f0ed34400ca41a56d704f6fac0612ad2
C compiler
0
18
111
96
2021-05-27T05:39:11Z
Mysterymath
3
Filled out C compiler documentation with more detail.
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from it's machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called [[Zero page]], which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as '''imaginary registers''', which not to be confused with [http://compilers.cs.ucla.edu/fernando/projects/soc/llvm_doc/regAlloc.html LLVM's virtual registers]. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, eferences to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
ae241fce0e491ea0fddb93846abf66eab81b2e9d
113
111
2021-05-27T05:54:37Z
Mysterymath
3
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
The compiler is a freestanding implementation of the C99 standard, with a couple caveats. The biggest is that it isn't finished yet!
Code generation is broken, both in terms of what compiles and in terms of whether or not the results actually work.
Development is ongoing though, and test cases are moving from failed to passing at a considerable clip!
Even for the initial release, there'll still be some caveats:
* While this is a freestanding implementation, we'll also provide a few support libraries; mostly just those needed to get LLVM's end-to-end test suite passing. This includes printf, sprintf, and alloca.
* While the float and double data types will be legal, any operations performed on them will emit calls to soft float routines that are ''not'' provided with the compiler. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project. In the mean time, because the libcall names are fairly standard, it should be possible for users to develop their own implementation if they see fit.
* The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines.
b3196f59453c03c8de7d07f9d9fb6bcfc4383051
ELF specification
0
15
116
60
2021-06-01T21:27:10Z
Jbyrd
1
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.2 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the ELF specification as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
df03a8a2ef05bb73b06fbf1f581364e8a8b1a7b8
117
116
2021-06-01T21:32:32Z
Jbyrd
1
/* Machine type field */
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
493a98082f07100a8d8c91a69a26a8a7f9b9804c
118
117
2021-06-01T21:45:40Z
Jbyrd
1
Relocation types filled out
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most signifcant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
4fc6b5307ab7caf7b5ff07cc878162a5dbefdc2a
119
118
2021-06-01T21:55:26Z
Jbyrd
1
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification. This specification uses [https://semver.org/ semantic numbering] to track changes.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This draft considers the [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2] to be normative.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, shall be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although ELF generally represents 32-bit or 64-bit executables, there's no reason why ELF code ''must'' use that entire memory range. In particular, MOS-compatible processors only utilize 16, or at most 24, bits of address space.
Unused bits in any 32-bit ELF address field should be set to zero. Setting them to any other value has undefined behavior.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most signifcant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
17bf8d73a26b8b1a40b7792700cd6f473f244b43
120
119
2021-06-01T21:58:47Z
Jbyrd
1
/* ELF specification for MOS-compatible processors */
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and similar processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, shall be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although ELF generally represents 32-bit or 64-bit executables, there's no reason why ELF code ''must'' use that entire memory range. In particular, MOS-compatible processors only utilize 16, or at most 24, bits of address space.
Unused bits in any 32-bit ELF address field should be set to zero. Setting them to any other value has undefined behavior.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most signifcant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
6e6e823cac8d4bd415a57ffb01dc33566276bcb6
121
120
2021-06-01T21:59:13Z
Jbyrd
1
/* Overview */
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, shall be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although ELF generally represents 32-bit or 64-bit executables, there's no reason why ELF code ''must'' use that entire memory range. In particular, MOS-compatible processors only utilize 16, or at most 24, bits of address space.
Unused bits in any 32-bit ELF address field should be set to zero. Setting them to any other value has undefined behavior.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are encouraged, but not required, to emit warnings indicating that a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations generally work as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most signifcant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
af801145ac3b21c7b630fb39c8ce48b7ab9b926f
122
121
2021-06-01T22:08:59Z
Jbyrd
1
/* ELF specification for MOS-compatible processors */
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most signifcant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
7b7f5d93d717b5fcdabce1df4034d23bf0d4a3b9
Assembler relaxation
0
19
123
82
2021-06-01T22:10:16Z
Jbyrd
1
Jbyrd moved page [[Zero page]] to [[Assembler relaxation]] without leaving a redirect: Assembler relaxation
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section zp
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x</code>
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
c2871207f364a0e425504f4b177c94247df44e8a
Getting started
0
23
127
2021-06-02T04:21:57Z
Jbyrd
1
Created page with "The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually bu..."
wikitext
text/x-wiki
The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually build the new target. If you like, you can speed up the build by disabling the other targets:
<code>-DLLVM_TARGETS_TO_BUILD="".</code>
It's early days for Clang and the LLVM code generator, but the assembler and linker (LLD) are both [https://www.productplan.com/glossary/minimum-viable-product MVP] complete. Both use ELF as their object format; the target-specific ELF definitions were extended to accommodate the 6502.
No libraries or platform support are included with the llvm-mos repository, to keep it a clean fork of LLVM. These are contained in the related [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk]. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can be used without the SDK. However, the various platform subtargets (e.g. apple2e, c64, etc) used by the compiler driver will require the SDK to be present. See the SDK for more information as it develops.
=== Getting the Source Code and Building ===
The LLVM Getting Started documentation may be out of date. The [https://clang.llvm.org/get_started.html Clang Getting Started page] might have more accurate information.
This is an example work-flow and configuration to get and build the LLVM source:
# Checkout LLVM (including related sub-projects like Clang):
#* <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
#* Or, on windows, <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
# Configure and build LLVM and Clang:
#* <code>cd llvm-project</code>
#* <code>cmake -S llvm -B build -G <generator> -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" [options]</code> Some common build system generators are:
#** <code>Ninja</code> --- for generating Ninja build files. Most llvm developers use Ninja.
#** <code>Unix Makefiles</code> --- for generating make-compatible parallel makefiles.
#** <code>Visual Studio</code> --- for generating Visual Studio projects and solutions.
#** <code>Xcode</code> --- for generating Xcode projects. Some Common options:
#** <code>-DLLVM_ENABLE_PROJECTS='...'</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use <code>-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"</code>.
#** <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
#** <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is Debug.
#** <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
#* <code>cmake --build build [-- [options] <target>]</code> or your build system specified above directly.
#** The default target (i.e. <code>ninja</code> or <code>make</code>) will build all of LLVM.
#** The <code>check-all</code> target (i.e. <code>ninja check-all</code>) will run the regression tests to ensure everything is in working order.
#** CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
#** Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
#* For more information see [https://llvm.org/docs/CMake.html CMake]
Consult the Getting Started with LLVM page for detailed information on configuring and compiling LLVM. You can visit Directory Layout to learn about the layout of the source code tree.
a608d570c20397a48301515a4b4885a0fdfec95d
128
127
2021-06-02T04:22:28Z
Jbyrd
1
wikitext
text/x-wiki
The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually build the new target. If you like, you can speed up the build by disabling the other targets: <code>-DLLVM_TARGETS_TO_BUILD=""</code>
It's early days for Clang and the LLVM code generator, but the assembler and linker (LLD) are both [https://www.productplan.com/glossary/minimum-viable-product MVP] complete. Both use ELF as their object format; the target-specific ELF definitions were extended to accommodate the 6502.
No libraries or platform support are included with the llvm-mos repository, to keep it a clean fork of LLVM. These are contained in the related [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk]. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can be used without the SDK. However, the various platform subtargets (e.g. apple2e, c64, etc) used by the compiler driver will require the SDK to be present. See the SDK for more information as it develops.
=== Getting the Source Code and Building ===
The LLVM Getting Started documentation may be out of date. The [https://clang.llvm.org/get_started.html Clang Getting Started page] might have more accurate information.
This is an example work-flow and configuration to get and build the LLVM source:
# Checkout LLVM (including related sub-projects like Clang):
#* <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
#* Or, on windows, <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
# Configure and build LLVM and Clang:
#* <code>cd llvm-project</code>
#* <code>cmake -S llvm -B build -G <generator> -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" [options]</code> Some common build system generators are:
#** <code>Ninja</code> --- for generating Ninja build files. Most llvm developers use Ninja.
#** <code>Unix Makefiles</code> --- for generating make-compatible parallel makefiles.
#** <code>Visual Studio</code> --- for generating Visual Studio projects and solutions.
#** <code>Xcode</code> --- for generating Xcode projects. Some Common options:
#** <code>-DLLVM_ENABLE_PROJECTS='...'</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use <code>-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"</code>.
#** <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
#** <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is Debug.
#** <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
#* <code>cmake --build build [-- [options] <target>]</code> or your build system specified above directly.
#** The default target (i.e. <code>ninja</code> or <code>make</code>) will build all of LLVM.
#** The <code>check-all</code> target (i.e. <code>ninja check-all</code>) will run the regression tests to ensure everything is in working order.
#** CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
#** Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
#* For more information see [https://llvm.org/docs/CMake.html CMake]
Consult the Getting Started with LLVM page for detailed information on configuring and compiling LLVM. You can visit Directory Layout to learn about the layout of the source code tree.
20eaa6e2805951d71e89000b71e5e74669155f96
129
128
2021-06-02T04:23:31Z
Jbyrd
1
wikitext
text/x-wiki
The MOS target is still considered experimental (and unfinished!), so you need to pass <code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS"</code> to cmake in order to actually build the new target. If you like, you can speed up the build by disabling the other targets: <code>-DLLVM_TARGETS_TO_BUILD=""</code>
It's early days for Clang and the LLVM code generator, but the [[assembler]] and [[linker]] (LLD) are both [https://www.productplan.com/glossary/minimum-viable-product MVP] complete. Both use [[ELF overview|ELF]] as their object format; the target-specific [[ELF definitions]] were extended to accommodate the 6502.
No libraries or platform support are included with the llvm-mos repository, to keep it a clean fork of LLVM. These are contained in the related [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk]. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can be used without the SDK. However, the various platform subtargets (e.g. apple2e, c64, etc) used by the compiler driver will require the SDK to be present. See the SDK for more information as it develops.
=== Getting the Source Code and Building ===
The LLVM Getting Started documentation may be out of date. The [https://clang.llvm.org/get_started.html Clang Getting Started page] might have more accurate information.
This is an example work-flow and configuration to get and build the LLVM source:
# Checkout LLVM (including related sub-projects like Clang):
#* <code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
#* Or, on windows, <code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
# Configure and build LLVM and Clang:
#* <code>cd llvm-project</code>
#* <code>cmake -S llvm -B build -G <generator> -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" [options]</code> Some common build system generators are:
#** <code>Ninja</code> --- for generating Ninja build files. Most llvm developers use Ninja.
#** <code>Unix Makefiles</code> --- for generating make-compatible parallel makefiles.
#** <code>Visual Studio</code> --- for generating Visual Studio projects and solutions.
#** <code>Xcode</code> --- for generating Xcode projects. Some Common options:
#** <code>-DLLVM_ENABLE_PROJECTS='...'</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use <code>-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"</code>.
#** <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
#** <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is Debug.
#** <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
#* <code>cmake --build build [-- [options] <target>]</code> or your build system specified above directly.
#** The default target (i.e. <code>ninja</code> or <code>make</code>) will build all of LLVM.
#** The <code>check-all</code> target (i.e. <code>ninja check-all</code>) will run the regression tests to ensure everything is in working order.
#** CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
#** Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
#* For more information see [https://llvm.org/docs/CMake.html CMake]
Consult the Getting Started with LLVM page for detailed information on configuring and compiling LLVM. You can visit Directory Layout to learn about the layout of the source code tree.
ac2de84c6ca0a92cf256bf4f786723c79e69a28f
SDK overview
0
24
131
2021-06-02T04:26:05Z
Jbyrd
1
Created page with "A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises..."
wikitext
text/x-wiki
A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises all the platform-specific libraries, includes, and examples that can be helpful using llvm-mos with a specific target.
56ef83a903abddad85663e53dfa87f77e0080055
Current status
0
25
135
2021-06-02T05:28:51Z
Jbyrd
1
Created page with "'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running..."
wikitext
text/x-wiki
'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== C compiler ==
An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Currently, it also has notable codegen bugs that prevents it from being used for any but the smallest programs, but progress is ongoing.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
319340240bf06be171582c310135511a7cd5ee47
136
135
2021-06-02T05:30:24Z
Jbyrd
1
wikitext
text/x-wiki
== Overview ==
'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== C compiler ==
An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Currently, it also has notable codegen bugs that prevents it from being used for any but the smallest programs, but progress is ongoing.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
7e8339f21beeedb2e6017adc60d878f4124b87a3
Rationale
0
26
138
2021-06-02T05:55:33Z
Jbyrd
1
Created page with "== Why yet another 6502 C compiler? == After all, there's cc65, and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient an..."
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's cc65, and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler.
5088d41f68daacb8b988ce9ca10a577ffc8f3c73
139
138
2021-06-02T06:04:42Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's cc65, and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
8768c18c918b6dd8edb2956ea7b4479779084247
140
139
2021-06-02T06:06:03Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and KickC, and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
a697458d207811c40e31678976e85993dd5691f3
141
140
2021-06-02T06:06:32Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
0d778062ab3523a536062a6df96bde9dee958738
142
141
2021-06-02T06:11:53Z
Jbyrd
1
/* Performance */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof in concept exists, demonstrating that llvm-mos can support other source languages as well, such as Rust.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
ae8b52a985fb9f737a8e1a54319509049721e129
143
142
2021-06-02T07:44:31Z
Jbyrd
1
/* Features */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and vbcc, and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that llvm-mos can support Rust as a source language. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
a626ae51055c12177889bd6b68a026f29694fdb7
144
143
2021-06-02T07:45:24Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that llvm-mos can support Rust as a source language. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
8ea9b81b171f658b4964b68a412e1d8d773c3481
145
144
2021-06-02T07:51:04Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
And llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
7ef85ef27c7df2ad3e135cbcc08a1378d3142adb
Building llvm-mos on Windows
0
27
150
2021-06-02T09:45:48Z
Jbyrd
1
Created page with "Generally speaking, builds of LLVM on Windows are finicky and opinionated, because LLVM is finicky and opinionated to build on Windows. I use a stable release of LLVM to compi..."
wikitext
text/x-wiki
Generally speaking, builds of LLVM on Windows are finicky and opinionated, because LLVM is finicky and opinionated to build on Windows. I use a stable release of LLVM to compile LLVM.
When checking out from git, ''it's important to make sure that core.autocrlf is set to False''. Windows installations strongly encourage you to set this to true, but if you do, then LLVM acceptance tests will fail.
To set up a Windows development and testing machine, I install Chocolatey first, at an administrator PowerShell prompt:
<code>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<nowiki>https://chocolatey.org/install.ps1'</nowiki>))</code>
Then, I run the following commands to install all prerequisites:
<code>choco install git.install --params "/NoAutoCrlf"
choco install cmake python llvm vscode ninja visualstudio2019community visualstudio2019-workload-nativedesktop</code>
The LLVM documentation suggests that you need the GnuWin32 utilities, but I don't think that's necessary; you can simply use the ones that the Windows version of git installs at <code>C:/Program Files/git/usr/bin</code> . You can point the LLVM_LIT_TOOLS_DIR build variable at that directory, and llvm-lit will use all the tools in that directory for building the check-all target.
As of this writing, my Windows build batch file looks like this:
<code>set "WORKSPACE_FORWARD_SLASH=%WORKSPACE:\=/%"
set LLVM_BIN=C:/Progra~1/LLVM/bin
cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=%WORKSPACE_FORWARD_SLASH%/build/install -DLLVM_LIT_TOOLS_DIR=C:/Progra~2/GnuWin32/bin -DCMAKE_AR=%LLVM_BIN%/llvm-ar -DCMAKE_RANLIB=%LLVM_BIN%/llvm-ranlib -DCMAKE_C_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_CXX_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_LINKER=%LLVM_BIN%/lld-link.exe -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all -DLLVM_LIT_TOOLS_DIR="C:/Program Files/git/usr/bin"
cmake --build . --config Release --target check-all
cmake --build . --config Release --target install</code>
cbb7957107d0e67660ecc64f49c9d461d7a17480
Building llvm-mos on MacOS
0
28
152
2021-06-02T09:46:39Z
Jbyrd
1
Created page with "The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed: <code>cmake ../../llvm -G Ninja -DLLVM_EXPERIMEN..."
wikitext
text/x-wiki
The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed:
<code>cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=${WORKSPACE}/build/stage2/install -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all
cmake --build . --config Release --target check-all
cmake --build . --config Release --target install</code>
94a9658278f686ce6cd5fb9568944019ff5a4a8d
Building llvm-mos on Windows
0
27
153
150
2021-06-02T09:48:30Z
Jbyrd
1
wikitext
text/x-wiki
Generally speaking, builds of llvm-mos on Windows are finicky and opinionated, because LLVM itself is finicky and opinionated to build on Windows. I use a stable release of LLVM to compile LLVM.
When checking out from git, ''it's important to make sure that core.autocrlf is set to False''. Windows installations strongly encourage you to set this to true, but if you do, then LLVM acceptance tests will fail.
To set up a Windows development and testing machine, I install Chocolatey first, at an administrator PowerShell prompt:
<code>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<nowiki>https://chocolatey.org/install.ps1'</nowiki>))</code>
Then, I run the following commands to install all prerequisites:
<code>choco install git.install --params "/NoAutoCrlf"
choco install cmake python llvm vscode ninja visualstudio2019community visualstudio2019-workload-nativedesktop</code>
The LLVM documentation suggests that you need the GnuWin32 utilities, but I don't think that's necessary; you can simply use the ones that the Windows version of git installs at <code>C:/Program Files/git/usr/bin</code> . You can point the LLVM_LIT_TOOLS_DIR build variable at that directory, and llvm-lit will use all the tools in that directory for building the check-all target.
As of this writing, my Windows build batch file looks like this:
<code>set "WORKSPACE_FORWARD_SLASH=%WORKSPACE:\=/%"
set LLVM_BIN=C:/Progra~1/LLVM/bin
cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=%WORKSPACE_FORWARD_SLASH%/build/install -DCMAKE_AR=%LLVM_BIN%/llvm-ar -DCMAKE_RANLIB=%LLVM_BIN%/llvm-ranlib -DCMAKE_C_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_CXX_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_LINKER=%LLVM_BIN%/lld-link.exe -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all -DLLVM_LIT_TOOLS_DIR="C:/Program Files/git/usr/bin"
cmake --build . --config Release --target check-all
cmake --build . --config Release --target install</code>
00d44c86153e39e9ce73648ee16a52916da0d9ca
182
153
2021-06-03T16:32:01Z
Jbyrd
1
wikitext
text/x-wiki
Generally speaking, builds of llvm-mos on Windows are finicky and opinionated, because LLVM itself is finicky and opinionated to build on Windows. I use a stable release of LLVM to compile LLVM.
When checking out from git, ''it's important to make sure that core.autocrlf is set to False''. Windows installations strongly encourage you to set this to true, but if you do, then LLVM acceptance tests will fail.
To set up a Windows development and testing machine, I install Chocolatey first, at an administrator PowerShell prompt:
<code>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<nowiki>https://chocolatey.org/install.ps1'</nowiki>))</code>
Then, I run the following commands to install all prerequisites:
<code>choco install git.install --params "/NoAutoCrlf"
choco install cmake python llvm vscode ninja visualstudio2019community visualstudio2019-workload-nativedesktop</code>
The LLVM documentation suggests that you need the GnuWin32 utilities, but I don't think that's necessary; you can simply use the ones that the Windows version of git installs at <code>C:/Program Files/git/usr/bin</code> . You can point the LLVM_LIT_TOOLS_DIR build variable at that directory, and llvm-lit will use all the tools in that directory for building the check-all target.
As of this writing, my Windows build batch file looks like this:
<code>set "WORKSPACE_FORWARD_SLASH=%WORKSPACE:\=/%"
set LLVM_BIN=C:/Progra~1/LLVM/bin
cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=%WORKSPACE_FORWARD_SLASH%/build/install -DCMAKE_AR=%LLVM_BIN%/llvm-ar -DCMAKE_RANLIB=%LLVM_BIN%/llvm-ranlib -DCMAKE_C_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_CXX_COMPILER=%LLVM_BIN%/clang-cl.exe -DCMAKE_LINKER=%LLVM_BIN%/lld-link.exe -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all -DLLVM_LIT_TOOLS_DIR="C:/Program Files/git/usr/bin"
cmake --build . --config Release --target check-all
cmake --build . --config Release --target install</code>
[[Category:Building]]
e1bc322eed324bc4cd0ae2fbe57a13f41ddf6e35
Welcome
0
1
154
151
2021-06-02T09:53:52Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Root]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
477dddf61d2846e5538e90b9130c8c8cfd711744
158
154
2021-06-02T09:59:59Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Root]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
<categorytree mode="pages">Root</categorytree>
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
671341ce6884dc2ddfece3896f40af0d5f9a00e6
159
158
2021-06-02T10:00:15Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Root]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
<categorytree mode="pages">Assembly</categorytree>
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
c125f426488b44d653c61e842dc34f18e7c1e75b
160
159
2021-06-02T10:01:30Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Root]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
<categorytree>Assembly</categorytree>
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
cc08b8f8298a3c8075721bf2660549d735c6392e
161
160
2021-06-02T10:01:56Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Root]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
477dddf61d2846e5538e90b9130c8c8cfd711744
164
161
2021-06-02T10:06:38Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Root]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
<CategoryTree>Main</CategoryTree>
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
b2629a2db43b270054692a37a8c56c729504eb32
165
164
2021-06-02T10:07:21Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
<CategoryTree>Main</CategoryTree>
=== Key topics ===
* Assembly
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
bdbf483d7f08e0f9d31cdb6ddb95568ef8b19fb8
168
165
2021-06-02T10:11:18Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
<CategoryTree>Main</CategoryTree>
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
fda1fc0974b75b3390835732b25cfac7b8592bc8
170
168
2021-06-02T10:14:50Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
9b66becdbaeebe4b0909440720e55a46fab6b11d
171
170
2021-06-02T10:18:36Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
<CategoryTree mode="pages" depth="3" hideroot="on" font-size="150%">Main</CategoryTree>
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*
44123b90783c6a84ecc1af4d891d50bc06080364
172
171
2021-06-02T10:22:43Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*Building llvm-mos
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
695fa4f69467836108fa5b1cee7fbc9d8ee07ccd
179
172
2021-06-03T16:30:33Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* C
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
e034fc3580bd4721204be9b1773e7c3fefecda09
183
179
2021-06-03T16:32:54Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[Code generation]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
469be1eebda4cac4d43bd58191f2d1cb337666af
188
183
2021-06-03T16:36:57Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*Code generation
**[[Code generation overview]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
0461d91a2ac00f828c62e8e5f809652fee93abef
189
188
2021-06-03T16:37:25Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
*[[Current status]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
5e778d979dc76e64b6e8e4c4e7343135d89c2bbc
193
189
2021-06-03T16:40:59Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[Emulator]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
204e57bd268162facf780ee732a31b85cd4879aa
197
193
2021-06-03T16:42:26Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** Emulator overview
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
a9efd2a2d62be6241a16939f1170f7c049e82d4a
199
197
2021-06-03T16:43:04Z
Jbyrd
1
/* Key topics */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
2b9e23b1d931a4a6d2d5cfee350996eef6cd0c4d
204
199
2021-06-03T16:47:36Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
[[Category:Linking]]
906847bd83b5da2361ad1f49f0b34125b383eeab
205
204
2021-06-03T16:47:57Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* Linking
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
2b9e23b1d931a4a6d2d5cfee350996eef6cd0c4d
Assembler
0
13
155
39
2021-06-02T09:55:17Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Assembler]]
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
e0e576f9bbc63b1804a7f17db9da650608b39fc9
156
155
2021-06-02T09:55:30Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Assembler]]
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
40ab4639e00ce8e4d400ab4173a089724b8557a6
162
156
2021-06-02T10:04:27Z
Jbyrd
1
wikitext
text/x-wiki
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
[[Category:Assembly]]
2b6b2bb2e90bbb26d46014b1ae18ebb969cfae4e
Category:Assembly
14
30
163
2021-06-02T10:05:14Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
Category:Main
14
31
166
2021-06-02T10:08:35Z
Jbyrd
1
Created blank page
wikitext
text/x-wiki
da39a3ee5e6b4b0d3255bfef95601890afd80709
Assembler relaxation
0
19
167
123
2021-06-02T10:10:45Z
Jbyrd
1
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section zp
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x</code>
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
2e2cb88eca46365156fa8d6d833091cebed532c3
Modifiers
0
16
169
109
2021-06-02T10:11:50Z
Jbyrd
1
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
[[Category:Assembly]]
f4672e19a864b314abf909548374bb43af9896b4
Rationale
0
26
173
145
2021-06-02T10:31:31Z
104.189.8.152
0
/* Performance */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
And, for those programs that do require a soft stack, llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
b8d79941520c20081844b8d43b0522510239b859
C compiler
0
18
174
113
2021-06-02T19:36:42Z
71.198.117.145
0
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
The compiler is a freestanding implementation of the C99 standard, with a couple caveats. The biggest is that it isn't finished yet!
Code generation is broken, both in terms of what compiles and in terms of whether or not the results actually work.
Development is ongoing though, and test cases are moving from failed to passing at a considerable clip!
Even for the initial release, there'll still be some caveats:
* While this is a freestanding implementation, we'll also provide a few support libraries; mostly just those needed to get LLVM's end-to-end test suite passing. This includes printf, sprintf, and alloca.
* No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project.
* The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines.
f2a29da6f9499d726337276163a06d4f77943cce
186
174
2021-06-03T16:34:47Z
Jbyrd
1
wikitext
text/x-wiki
A backend has been added to clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
The compiler is a freestanding implementation of the C99 standard, with a couple caveats. The biggest is that it isn't finished yet!
Code generation is broken, both in terms of what compiles and in terms of whether or not the results actually work.
Development is ongoing though, and test cases are moving from failed to passing at a considerable clip!
Even for the initial release, there'll still be some caveats:
* While this is a freestanding implementation, we'll also provide a few support libraries; mostly just those needed to get LLVM's end-to-end test suite passing. This includes printf, sprintf, and alloca.
* No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project.
* The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines.
[[Category:C]]
066607b000a7cfcdbfe2faca465a643159c274ab
Code generation overview
0
22
175
148
2021-06-02T19:40:15Z
71.198.117.145
0
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit constant and 8-bit offset. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
d7b8c8471961a386cddee90176cf509c748e545b
176
175
2021-06-02T19:41:31Z
71.198.117.145
0
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit constant and 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
ef8883effcd734f6a2cafe2168df12577966159d
177
176
2021-06-02T19:42:20Z
71.198.117.145
0
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit constant and 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
8335bf29df7f7dfdf30fa57b229dc852cd872af7
178
177
2021-06-02T19:43:00Z
71.198.117.145
0
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
c64768a0e0ff876ac705875ab0fb78fe148bfbe5
187
178
2021-06-03T16:36:17Z
Jbyrd
1
Jbyrd moved page [[Code generation]] to [[Code generation overview]] without leaving a redirect
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
c64768a0e0ff876ac705875ab0fb78fe148bfbe5
191
187
2021-06-03T16:38:06Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a series of individual 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
[[Category:Code generation]]
fd4db28d07cad6d1527a4b7cc9c21f5a46c53c7b
Category:Building
14
32
180
2021-06-03T16:30:54Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
Building llvm-mos on MacOS
0
28
181
152
2021-06-03T16:31:36Z
Jbyrd
1
wikitext
text/x-wiki
The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed:
<code>cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=${WORKSPACE}/build/stage2/install -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all
cmake --build . --config Release --target check-all
cmake --build . --config Release --target install</code>
[[Category:Building]]
18f98c093de0e432e24b65e5fa605a71d63c875a
Category:C
14
33
184
2021-06-03T16:33:22Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
C calling convention
0
17
185
110
2021-06-03T16:33:44Z
Jbyrd
1
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each imaginary (zero page) register, increasing.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers. If none are available, the bytes are split and passed as above.
* If no registers are available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
0256ea557f1b753a17c64b1be7f43c5fbbde1cf8
Category:Code generation
14
34
190
2021-06-03T16:37:40Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
Debugging code generation
0
20
192
100
2021-06-03T16:38:26Z
Jbyrd
1
wikitext
text/x-wiki
The llvm-mos code generator runs mostly on test cases written in MIR. These can be found in <code>llvm/test/CodeGen/MOS</code> .
To test a particular section of codegen, you typically compile the code with clang, and then use llc to watch as the GlobalISel architecture lowers the code from generic MIR to MOS code.
One way of doing this is by debugging program:
llc -O2 -debug --debug-pass=Details -verify-machineinstrs -o %t %s
Where %t is the assembly output file, and %s is the name of a LLVM bitcode file with an .ll extension.
This command line runs all the GlobalISel passes, emitting copious debug information on every pass.
A formula for creating C code to test codegen passes is as follows:
clang --target=mos -emit-llvm -S %s -o %t.ll
llc -debug --debug-pass=Details -mtriple=mos -verify-machineinstrs %t.ll -o %t.s
[[Category:Code generation]]
e04fd7a9b3198118f2075f7ff8b099013515472f
Emulator overview
0
21
194
101
2021-06-03T16:41:12Z
Jbyrd
1
Jbyrd moved page [[Emulator]] to [[Emulator overview]]
wikitext
text/x-wiki
As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well as test suite verification, without requiring actual hardware to be present.
The intention of the emulator design is to permit multiple future iterations of emulation, without throwing away any existing functionality. Although a classic read-parse-execute style emulator would be sufficient for small tests, we'd want to be able to transpile generator code to the local machine's native instruction set, for maximum performance. We'd like to leave the door open to debugging with lldb or other tools, including reverse execution. And although all known LLVM targets have register sets and contiguous memory, we don't want to necessarily require any particular emulation to have these features.
About 80% of the functionality required to build an emulator, is already present within LLVM for most targets. LLVM already includes a disassembler class, MCDisassembler, which is implemented for most platforms. It's able to reconstruct a series of MCInst objects given a raw instruction stream. So, what's needed is a way to take the "current" MCInst, and emulate the behavior of it.
It's also natural to describe the behavior of each instruction, in the tablegen file that defines it. It allows similar instructions to be grouped and to share related code for related activities.
Since LLVM's tablegen already has complete knowledge of all the instructions present for the target, it makes sense to explain the emulator behavior of these instructions to tablegen, and then let tablegen collate all these behaviors into an .inc file that can be included within a larger emulator.
In keeping with the LLVM philosophy, MCEmulator is a set of building blocks for building emulators. It includes a command-line tool, llvm-emu, which demonstrates one way to combine those blocks into an emulator.
2942e7c8cb674e23ed075ec468c16d7598b71ca1
200
194
2021-06-03T16:43:51Z
Jbyrd
1
wikitext
text/x-wiki
As part of the llvm-mos project, general-purpose emulator functionality is being added to LLVM. This permits quick testing and experimentation with compiler changes, as well as test suite verification, without requiring actual hardware to be present.
The intention of the emulator design is to permit multiple future iterations of emulation, without throwing away any existing functionality. Although a classic read-parse-execute style emulator would be sufficient for small tests, we'd want to be able to transpile generator code to the local machine's native instruction set, for maximum performance. We'd like to leave the door open to debugging with lldb or other tools, including reverse execution. And although all known LLVM targets have register sets and contiguous memory, we don't want to necessarily require any particular emulation to have these features.
About 80% of the functionality required to build an emulator, is already present within LLVM for most targets. LLVM already includes a disassembler class, MCDisassembler, which is implemented for most platforms. It's able to reconstruct a series of MCInst objects given a raw instruction stream. So, what's needed is a way to take the "current" MCInst, and emulate the behavior of it.
It's also natural to describe the behavior of each instruction, in the tablegen file that defines it. It allows similar instructions to be grouped and to share related code for related activities.
Since LLVM's tablegen already has complete knowledge of all the instructions present for the target, it makes sense to explain the emulator behavior of these instructions to tablegen, and then let tablegen collate all these behaviors into an .inc file that can be included within a larger emulator.
In keeping with the LLVM philosophy, MCEmulator is a set of building blocks for building emulators. It includes a command-line tool, llvm-emu, which demonstrates one way to combine those blocks into an emulator.
[[Category:Emulator]]
48a63f41f6eee82544383c051795749a800156ae
Category:Emulator
14
36
198
2021-06-03T16:42:42Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
Getting started
0
23
201
129
2021-06-03T16:45:37Z
Jbyrd
1
wikitext
text/x-wiki
WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS.
To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk.
For more information about this project, please see llvm-mos.org.
For information about the current status of this project, please see [[Current status|Current status.]]
To learn why this project exists, please see [[Rationale]].
= Getting started =
== Download the LLVM-MOS tools ==
If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform:
* MacOS
* Linux
* Windows
These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality.
== Or, build the LLVM-MOS tools ==
However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform.
Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM.
Once you meet those requirements, you may use the following formula within your build environment:
=== Clone the LLVM-MOS repository ===
On Linux and MacOS:
<code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
On Windows:
<code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
If you fail to use the --config flag as above, then verification tests will fail on Windows.
=== Configure the LLVM-MOS project ===
<code>cd llvm-mos
cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code>
This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment.
Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake.
* <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command.
* <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests.
* <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
* <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file.
* <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
=== Build the LLVM-MOS project ===
<code>cmake --build build [-- [options] <target>]</code>
The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution.
CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
= Help us out =
We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes.
All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules.
If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality.
You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests.
Additionally, the current state of our documentation at <nowiki>https://llvm-mos.org</nowiki> can always use improvements and clarifications.
fb98105011fe7b3159611016d00cf1e7da6de775
202
201
2021-06-03T16:46:11Z
Jbyrd
1
wikitext
text/x-wiki
WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS.
To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk.
For more information about this project, please see llvm-mos.org.
For information about the current status of this project, please see [[Current status|Current status.]]
To learn why this project exists, please see [[Rationale]].
= Getting started =
== Download the LLVM-MOS tools ==
If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform:
* MacOS
* Linux
* Windows
These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality.
== Or, build the LLVM-MOS tools ==
However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform.
Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM.
Once you meet those requirements, you may use the following formula within your build environment:
=== Clone the LLVM-MOS repository ===
On Linux and MacOS:
<code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
On Windows:
<code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
If you fail to use the --config flag as above, then verification tests will fail on Windows.
=== Configure the LLVM-MOS project ===
<code>cd llvm-mos
cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code>
This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment.
Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake.
* <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command.
* <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests.
* <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
* <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file.
* <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
=== Build the LLVM-MOS project ===
<code>cmake --build build [-- [options] <target>]</code>
The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution.
CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
= Help us out =
We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes.
All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules.
If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality.
You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests.
Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications.
7bfc8ccbdb2a6e5f3986c5e3ef7958c54e66bf82
203
202
2021-06-03T16:46:54Z
Jbyrd
1
wikitext
text/x-wiki
WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS.
To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk.
For more information about this project, please see llvm-mos.org.
For information about the current status of this project, please see [[Current status|Current status.]]
To learn why this project exists, please see [[Rationale]].
= Getting started =
== Download the LLVM-MOS tools ==
If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform:
* MacOS
* Linux
* Windows
These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality.
== Or, build the LLVM-MOS tools ==
However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform.
Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM.
Once you meet those requirements, you may use the following formula within your build environment:
=== Clone the LLVM-MOS repository ===
On Linux and MacOS:
<code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
On Windows:
<code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
If you fail to use the --config flag as above, then verification tests will fail on Windows.
=== Configure the LLVM-MOS project ===
<code>cd llvm-mos
cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code>
This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment.
Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake.
* <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command.
* <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests.
* <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
* <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file.
* <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
=== Build the LLVM-MOS project ===
<code>cmake --build build [-- [options] <target>]</code>
The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution.
CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
= Help us out =
We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes.
All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules.
If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality.
You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests.
Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications.
[[Category:Main]]
7f6a87cec9708ca87a2c082ed2cddaa5e778b6f8
Welcome
0
1
206
205
2021-06-03T16:48:20Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* [[:Category:Linking|Linking]]
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
72f14374a3d50380ae388eabf96d72f427161095
210
206
2021-06-03T16:55:17Z
Jbyrd
1
/* Category tree (experimental) */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* [[:Category:Linking|Linking]]
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (experimental) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== All pages 14 ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
=== Categories ===
{{Special:Categories}}
026e26f9bd6512edf88acd22086ff33d5ab89903
211
210
2021-06-03T16:58:30Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* [[:Category:Linking|Linking]]
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (requires Ajax and Javascript) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
09543ce43fefe79b3f9506adaa2cec40034d7b1f
217
211
2021-06-03T17:06:13Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Key topics ===
* [[:Category:Assembly|Assembly]]
**[[Assembler]]
**[[Assembler relaxation|Assembler relaxation (how to put stuff in zero page)]]
***
**[[Modifiers]]
*[[:Category:Building|Building]]
**[[Building llvm-mos on MacOS|On MacOS]]
**[[Building llvm-mos on Windows|On Windows]]
* [[:Category:C|C]]
**[[C calling convention]]
**[[C compiler]]
**[https://clang.llvm.org/docs/ClangCommandLineReference.html clang command line reference]
*[[:Category:Code generation|Code generation]]
**[[Code generation overview]]
**[[Debugging code generation]]
* [[:Category:Emulator|Emulator]]
** [[Emulator overview]]
* [[Getting started]]
* [[:Category:Linking|Linking]]
**[[ELF overview]]
**[[ELF specification]]
*[[Rationale]]
*[[SDK]]
*[[Current status|Status]]
=== Category tree (requires Ajax and Javascript) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
ffdf629143606fce33dfae7504afe12a1a106341
218
217
2021-06-03T17:06:38Z
Jbyrd
1
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Category tree (requires Ajax and Javascript) ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
1c617f3b41e088261a6b91c50205964bbadce544
219
218
2021-06-03T17:07:45Z
Jbyrd
1
/* Category tree (requires Ajax and Javascript) */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out!
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
57152ad5445e9dc59ba5a791a1e56d5f36f84b4e
220
219
2021-06-03T17:12:02Z
104.189.8.152
0
Changed a period
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing development discussions occur [https://llvm-mos.slack.com/ on Slack, in the llvm-mos workspace]. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then please join our Slack group and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
f812f9aa3aa965cd6bb817e3d6544655a8aa11e7
227
220
2021-06-16T18:14:07Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[Category:Main]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
a25b30266412ac43e71f4a8aa3bfd0cfb2a67aec
230
227
2021-06-17T00:04:07Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
7862bee1b2af144660596210cba33093ea20acd8
231
230
2021-06-17T06:00:55Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
f04fbab6c3d100fe3e9e4e061dc24654366d7b34
235
231
2021-06-20T20:37:31Z
104.189.8.152
0
Edited a sentence
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
f24fd73bf0cb64e4eed8167aba5a65f368fe6e41
236
235
2021-06-20T20:37:55Z
104.189.8.152
0
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos project is not only able to compile short C and [https://llvm.org/docs/LangRef.html LLVM-IR] programs to 6502 assembly; it also has feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
f04fbab6c3d100fe3e9e4e061dc24654366d7b34
239
236
2021-07-01T23:16:20Z
71.198.117.145
0
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatibile with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
2adf4021f2d6e29331e96b67733a72cc831c1f9a
249
239
2021-07-16T20:51:42Z
71.198.117.145
0
Add trademark disclaimer.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatibile with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-rtaxxsdu-~3tSQaQCQjLmc27OVX5vsA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
1aeebcd17fe2496b4fbe6ae17f55495f0cf5626b
250
249
2021-07-19T19:47:51Z
Jbyrd
1
Updated Slack group to non expiring.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatibile with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
e3b03c755adeff21541aa8396865023e3bb16bae
251
250
2021-07-19T21:15:22Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs, including the . The llvm-mos Clang is nearly compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
6fa014598f63a6aa51c6286c3cde354df7dce4d8
Category:Linking
14
37
207
2021-06-03T16:48:38Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
ELF overview
0
14
208
61
2021-06-03T16:49:02Z
Jbyrd
1
wikitext
text/x-wiki
Both the assembler and the linker support [[wikipedia:Executable_and_Linkable_Format|ELF]], for object files, libraries, and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
You can use any of your favorite existing ELF tools to inspect or understand files generated by llvm-mos. The [https://llvm.org/docs/CommandGuide/llvm-readobj.html llvm-readobj], [https://llvm.org/docs/CommandGuide/llvm-objdump.html llvm-objdump], [https://llvm.org/docs/CommandGuide/llvm-objcopy.html llvm-objcopy], [https://llvm.org/docs/CommandGuide/llvm-strip.html llvm-strip], and likely the other command line tools as well, work as expected.
Although they don't necessarily know anything about the MOS processor line, this also means that ''generic'' tools that process ELF files, such as this [http://www.sunshine2k.de/coding/javascript/onlineelfviewer/onlineelfviewer.html online ELF viewer], can read and dump information about MOS executables, such as symbol tables, fixups, and relocation entries.
There exists a more precise [[ELF specification]] for llvm-mos.
[[Category:Linking]]
21fa26fbced5206c87230311cfea30a56588e183
ELF specification
0
15
209
122
2021-06-03T16:49:20Z
Jbyrd
1
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most signifcant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[[Category:Linking]]
ab0dd4909e503adccd7dcc767701c195e7361647
Rationale
0
26
212
173
2021-06-03T17:01:54Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
And, for those programs that do require a soft stack, llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
[[Category:Main]]
43a01164f18f40956cf9856835164519939166eb
233
212
2021-06-20T08:30:33Z
Jbyrd
1
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
Even at this early date, llvm-mos can find codegen optimizations that seem almost magical to mortal eyes.
By giving codegen control over a chunk of zero page memory, llvm-mos can treat that memory as imaginary registers for codegen purposes.
This makes the majority of emitted code into two-byte 6502 instructions, which are generally around a third faster, and a third smaller, than their three-byte counterparts.
Previously, it was thought that performant code in C on the 6502 was impossible, because all 6502 programs required a C-style soft stack. llvm-mos's greedy register allocator makes it so that many 6502 programs use no 16-bit stack whatsoever. Instead, these programs use zero-page with near-ideal allocation.
And, for those programs that do require a soft stack, llvm-mos's IndexIV pass lowers most pointer-plus-offset calculations -- generally a performance killer for any C compiler -- into a single native instruction on the 6502.
llvm-mos's lane-tracking register allocator is obsessive about limiting live variables to zero page, and generally does a better job on large programs than any human might.
llvm-mos's link-time optimization pass can remove dead code and perform interprocedural optimization at a level previously impossible for the 6502.
Even though this project is still very early, thanks to the GlobalISel architecture, llvm-mos knows more codegen optimization tricks than any existing 6502 compiler. This added performance will enable new applications and games for the 6502, which were previously impossible to build.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
== Findings ==
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," it is not a good host for C. In truth, the 6502 is an excellent target for C, ''if'' the compiler is designed from the ground up to take advantage of the 6502's architecture. By that, I mean that the compiler must be aware of zero page, and it must be able to manage a user-specified chunk of it. The original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. llvm-mos uses an "imaginary register" abstraction to produce performant 6502 code, by permitting the compiler to manage that zero-page memory in a manner compatible with all existing 6502 targets.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. In fact, source language representation is not a limiting factor in generating performant 6502 code. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
All these "common knowledge" assumptions have been demonstrated to be unsupportable, by counterexample.
[[Category:Main]]
7ad8424d85e0becb34521a56567f92ca7f8192bd
241
233
2021-07-01T23:43:13Z
71.198.117.145
0
/* Performance */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program.
At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers.
Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow).
As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
== Findings ==
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We presenting these to LLVM as registers, which makes our backend look roughly like a slightly odd backend, not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, there not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
cb55694452b692411b796ab881915f072756b032
242
241
2021-07-01T23:44:26Z
71.198.117.145
0
/* Findings */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program.
At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers.
Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow).
As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
== Findings ==
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, there not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
f6ca331258a2744fcb0e18cd1f28817f5bc03efa
243
242
2021-07-01T23:45:08Z
71.198.117.145
0
/* Findings */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program.
At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers.
Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow).
As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
== Findings ==
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
a8d8b9f9cf3a5d2d6d6f0deb3811cd98f5be0541
SDK overview
0
24
213
131
2021-06-03T17:02:39Z
Jbyrd
1
Jbyrd moved page [[SDK]] to [[SDK overview]] without leaving a redirect
wikitext
text/x-wiki
A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises all the platform-specific libraries, includes, and examples that can be helpful using llvm-mos with a specific target.
56ef83a903abddad85663e53dfa87f77e0080055
214
213
2021-06-03T17:02:54Z
Jbyrd
1
wikitext
text/x-wiki
A software development kit, [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], is in the early stages of development, to be compatible with llvm-mos. This SDK comprises all the platform-specific libraries, includes, and examples that can be helpful using llvm-mos with a specific target.
[[Category:SDK]]
fc724141d85b798d5ad28ec62cf0aea58de57d27
Category:SDK
14
38
215
2021-06-03T17:03:32Z
Jbyrd
1
Created page with "[[Category:Main]]"
wikitext
text/x-wiki
[[Category:Main]]
f429e0e8fafc74c22b63006cc728e3bdd2e9c299
Current status
0
25
216
136
2021-06-03T17:04:10Z
Jbyrd
1
wikitext
text/x-wiki
== Overview ==
'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== C compiler ==
An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Currently, it also has notable codegen bugs that prevents it from being used for any but the smallest programs, but progress is ongoing.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
08e9fbebeedde9e852b2c29809830c44419b45d5
232
216
2021-06-20T08:22:36Z
Jbyrd
1
/* C compiler */
wikitext
text/x-wiki
== Overview ==
'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== C compiler ==
An architecturally complete, but not feature complete, backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it's capable of compiling some short C programs for the 65xx targets. Recently, the compiler produced correct results on a slightly modified version of the gcc C torture test suite.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
c0dbd9f49e78c945c74f0c65546987de34a32eb9
237
232
2021-07-01T05:44:54Z
71.198.117.145
0
/* C compiler */
wikitext
text/x-wiki
== Overview ==
'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== C compiler ==
An C99-complete backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still fairly good, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
4a08c02605bf0df5eabdf6f250e3cb7412a62a34
238
237
2021-07-01T05:45:19Z
71.198.117.145
0
/* C compiler */
wikitext
text/x-wiki
== Overview ==
'''Warning:''' As of this writing, the LLVM-MOS compiler and toolchain is '''not feature complete.''' You should not expect to drop it into your project and generate running programs immediately.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== C compiler ==
An C99-complete backend to clang is being developed based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
A backend for MOS architectures has been added to llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause llvm-mc to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
52badda89a37baad2bd0f26ff8a14d527af03c39
C calling convention
0
17
221
185
2021-06-06T23:36:30Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255).
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the bytes are split and passed as above.
* If no registers are available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
5874c9125567c4388b3c15c916e7987b2732eb2d
222
221
2021-06-06T23:36:59Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255).
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above, low byte first.
* If no registers are available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
d7fa80068d97934353134703bfe2964831927285
223
222
2021-06-06T23:37:21Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255).
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers are available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
10c1c49e7ea74897399d46a645ab9635ede36b8c
224
223
2021-06-06T23:37:47Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then Y, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255).
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
84b950781a84a38e8375337a496414a5077a9619
225
224
2021-06-09T16:45:17Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). Y is not used for arguments (but is still caller-save); it's reserved to help the compiler shuffle values into the appropriate locations around calls.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
d13747dcebcd52ab28f0f5b0be866b402a823901
226
225
2021-06-11T23:22:25Z
104.189.8.152
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
6cf1d2653319093a1119f4d67ad570e0e6ca34d2
244
226
2021-07-05T20:21:48Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
* Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
58b87dd738bf3e9d76240b422c28e96d17311589
245
244
2021-07-06T06:25:52Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* The very last imaginary pointer register is skipped, although it is still caller-saved.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
* Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
7651bb5b9a113e9c051e3fd34ce784cfb25718f3
246
245
2021-07-06T20:28:31Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The bytes composing numeric arguments are passed individually in registers. The order used is A, then X, then each available imaginary (zero page) register, increasing (i.e., RC0 to RC255). The Y register is not used for arguments, but Y is still caller-saved; it's reserved to help the compiler shuffle values into the appropriate locations around calls.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* The callee-saved imaginary registers, RS2 (i.e., RC4 and RC5) and RS4 (i.e., RC8 and RC9) are skipped.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* RS2 and RS4 (and subregisters) are callee-saved. All other ZP locations, registers, and flags are caller-saved. The gap between the callee-saved registers balances between caller- and callee-saved registers if very little of the zero page is available.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
* Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
58b87dd738bf3e9d76240b422c28e96d17311589
Code generation overview
0
22
228
191
2021-06-16T23:56:10Z
Jbyrd
1
wikitext
text/x-wiki
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a small handful of 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
[[Category:Code generation]]
f6a7fb4407b457c1e01605716ee4f948c77aadf2
File:Rust-hello-atari-800.png
6
39
229
2021-06-17T00:02:59Z
Jbyrd
1
wikitext
text/x-wiki
Hello world and factorial calculation in Rust, for Atari 800, with proof of concept by mrk
2c220834e8a733bf20a16675f693daeab4377e3c
Getting started
0
23
234
203
2021-06-20T10:33:06Z
104.189.8.152
0
/* Download the LLVM-MOS tools */
wikitext
text/x-wiki
WARNING! As of this writing, this compiler and toolchain ''has signficant bugs''. You should not expect to drop it into your project and generate running programs immediately. Because this experiment is still early, this compiler should ''not'' be publicly reviewed, compared, or benchmarked against other compilers at this time. Until we change this warning, we disclaim any performance metric regarding LLVM-MOS.
To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk.
For more information about this project, please see llvm-mos.org.
For information about the current status of this project, please see [[Current status|Current status.]]
To learn why this project exists, please see [[Rationale]].
= Getting started =
== Download the LLVM-MOS tools ==
If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform:
* [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS]
* [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Linux]
* [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows]
These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality.
== Or, build the LLVM-MOS tools ==
However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform.
Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM.
Once you meet those requirements, you may use the following formula within your build environment:
=== Clone the LLVM-MOS repository ===
On Linux and MacOS:
<code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
On Windows:
<code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
If you fail to use the --config flag as above, then verification tests will fail on Windows.
=== Configure the LLVM-MOS project ===
<code>cd llvm-mos
cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code>
This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment.
Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake.
* <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command.
* <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests.
* <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
* <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file.
* <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
=== Build the LLVM-MOS project ===
<code>cmake --build build [-- [options] <target>]</code>
The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution.
CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
= Help us out =
We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes.
All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules.
If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality.
You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests.
Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications.
[[Category:Main]]
1a48d0086027c6f5bb9855216515cd83e42f68fc
C compiler
0
18
240
186
2021-07-01T23:19:16Z
71.198.117.145
0
wikitext
text/x-wiki
A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
The Clang is nearly compatible with the freestanding portion of the C99 standard, with a couple caveats:
* Although LLVM's (SingleSource) end-to-end test suite passes, we haven't finished auditing the compiler for C99 compatibility. It's likely already very compatible, but we haven't done the "full point inspection" yet.
* No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project.
* The (default) included printf will not be compiled with floating point support, even when we ship soft float libraries. We'll find some way to link in a different version if users elect to link the soft float routines.
[[Category:C]]
d45d00232d1b221611a0258816ad69e81a31e528
C Inline Assembly
0
40
247
2021-07-16T16:44:51Z
Mysterymath
3
Created page with "Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There a..."
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: 'o', 'v', '<', '>', and 'g'
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* 'a': The A register.
* 'x': The X register.
* 'y': The Y register.
* 'R': One of A, X, or Y.
* 'd': Either X or Y.
[[Category:C]]
9577a35ad758f4e082622ed00ad287d5a73e5153
248
247
2021-07-16T16:52:46Z
Mysterymath
3
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
[[Category:C]]
760d9a23c80290b5a2b9e22826867cfc946f35fe
C interrupts
0
41
252
2021-07-31T23:35:26Z
Mysterymath
3
Created page with "== Normal C Interrupt Handling == The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compi..."
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Large "Register" File ===
llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast.
We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow.
If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by two or more ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the main interrupt attribute mostly useful for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
=== Program-Wide Calling Convention Modification ===
If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved.
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
c2c091ca5cdd3aec110b52d4aedf5975009c5795
253
252
2021-07-31T23:37:31Z
Mysterymath
3
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Large "Register" File ===
llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast.
We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow.
If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by two or more ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the main interrupt attribute mostly useful for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
=== Program-Wide Calling Convention Modification ===
If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved.
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
0b53d70cf6db18feba7be26c91541bb3c5740ceb
254
253
2021-08-01T00:05:07Z
Mysterymath
3
/* "interrupt_norecurse" Attribute */
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Large "Register" File ===
llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast.
We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow.
If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the main interrupt attribute mostly useful for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
=== Program-Wide Calling Convention Modification ===
If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved.
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
18d56dd1e1b0b6e047d353e1420cc20bbe3a8720
255
254
2021-08-01T00:07:26Z
Mysterymath
3
/* "interrupt_norecurse" Attribute */
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Large "Register" File ===
llvm-mos treats the zero page as registers, and these locations participate in the C calling convention. However, there are rather a lot more than 32 on most platforms; anywhere from 60 to 255. Saving and restoring all of them presents a considerable burden, especially in the context of an interrupt handler, which needs to be fast.
We could make all but a few of these registers callee-saved, but this would lead to the compiler quite rarely using them; it wouldn't often be worth it to save and restore them. If we made most of them caller saved, then interrupt handlers (that call a single C function) become impossibly slow.
If there are no interrupt handlers in a program, the answer is obvious: make all but a few caller-saved, and the compiler can take advantage of as many as it desires. But if there's even a single interrupt anywhere in the program, we'd need the opposite convention: all but a few are callee-saved.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. A handful of Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
=== Program-Wide Calling Convention Modification ===
If an "interrupt" or "interrupt_norecurse" attribute is used anywhere within the program, then the calling convention of the entire program will change. All zero-page pointers past the first 5 become callee saved instead of caller-saved.
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
e2eed37d10346913fb19b8ae0f17eb18031f7b92
Welcome
0
1
256
251
2021-08-13T16:43:19Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is nearly compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project will permit modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler will be ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
11a1800b485ca954c562a9dad33480316d5b390f
273
256
2021-09-18T20:30:25Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://llvm.org/docs/WritingAnLLVMBackend.html backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang appears to be compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and as such, our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While our focus is currently feature-completeness, not optimization, we've already overcome all existing theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
469219ff97c9d171ce872d4cbd3f9d45c8cc5e59
299
273
2022-01-27T04:35:37Z
71.198.117.145
0
Update wording on the welcome page.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
The llvm-mos project is intended to create a first-class [https://github.com/llvm-mos backend] in [https://llvm.org/ LLVM] for the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [[Getting started]].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99, and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
The development team has established a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
We provide current builds of the main branch of the llvm-mos tool chain for [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows], [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS], and [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Ubuntu Linux].
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
90bbb9e727f2c897fad281c40ee148d4f4630560
304
299
2022-02-25T18:27:38Z
71.198.117.145
0
Update the welcome page in preparation for initial release.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], and the [[wikipedia:Atari_8-bit_family|Atari 8-bit family]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
536020405a372f37ed9f53d49b41eb1d342f54bd
ELF specification
0
15
257
209
2021-08-13T16:46:33Z
Jbyrd
1
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.3 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|A PC-relative jump value. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[[Category:Linking]]
5a9eb95ce55d163e45be12d2f83480c6275f7974
274
257
2021-09-19T18:09:44Z
Jbyrd
1
Better description of PCREL_8
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.4 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[[Category:Linking]]
537f39122d2dab5e6727ee34dbb0b77cee1eaa4f
296
274
2022-01-17T20:18:58Z
Jackoalan
5
Add R_MOS_ADDR_ASCIZ
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.4 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|14
|A null-terminated, decimal ASCII string of the value. Primary use case is for generating the BASIC header on C64.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[[Category:Linking]]
8bde310bc8751253c60dac3ce9772fb2164fa8ba
298
296
2022-01-17T20:27:12Z
Jackoalan
5
.mos_addr_asciz reference
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.4 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|.mos_addr_asciz <expression>, <num-digit-chars>
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|14
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[[Category:Linking]]
cf88593d6bd7aa3fb14167b95480f0a6a9df5336
Imaginary registers
0
42
258
2021-08-15T00:49:27Z
Jbyrd
1
Created page with "The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify. Like all modern c..."
wikitext
text/x-wiki
The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify.
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers.
Because "virtual registers" has another predefined meaning in LLVM land, we use the term '''imaginary register''' to refer to a byte in zero page memory. It's represented at codegen time by a symbol like __rc17 or __rs5, which is then translated to an actual memory address by the linker at link time.
The number of imaginary registers is user-selectable. To decide how many registers LLVM-MOS should use, use the --num-zp-regs=64 flag to clang, replacing 64 with the number of usable zero page locations. clang will only emit references to imaginary registers in the range from __rc0 to __rc[N-1]. This is how LLVM-MOS can be made to generate optimal output for very different 65xx architectures.
LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
6a115d12997969014e9eaa8b01eb2797e11f1c99
294
258
2021-11-16T07:37:06Z
71.198.117.145
0
wikitext
text/x-wiki
The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify.
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers.
Because "virtual registers" has another predefined meaning in LLVM land, we use the term '''imaginary register''' to refer to a byte in zero page memory. It's represented at codegen time by a symbol like __rc17 or __rs5, which is then translated to an actual memory address by the linker at link time.
Presently, the compiler requires 16 imaginary pointers (two contiguous bytes each), and the compiler doesn't presently have the means to make use of more. In the future, the compiler should make use of otherwise unused sections of the zero page for temporaries, local, and global variables.
LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
195555eb674ab8cb0ec439d1e60d9c3f0f776e9d
C interrupts
0
41
259
255
2021-08-15T04:57:29Z
71.198.117.145
0
Remove calling convention shenanigans.
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
f476e420d2c2a3dee25b35dc6ee3c774d6060495
262
259
2021-08-17T04:36:34Z
71.198.117.145
0
/* "no_isr" Attribute */
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly.<syntaxhighlight lang="6502tasm">
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda __save_a
pha
lda __save_x
pha
lda __save_y
pha
lda __call_indir
pha
lda __call_indir+1
pha
JSR body
pla
sta __call_indir+1
pla
sta __call_indir
pla
sta __save_y
pla
sta __save_x
pla
sta __save_a
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
9a3e45e86efba9867c00a994db92bf63d01648fb
263
262
2021-08-17T04:41:29Z
71.198.117.145
0
/* Manual Interrupt Sequence */
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm">
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda __save_a
pha
lda __save_x
pha
lda __save_y
pha
lda __call_indir
pha
lda __call_indir+1
pha
JSR body
pla
sta __call_indir+1
pla
sta __call_indir
pla
sta __save_y
pla
sta __save_x
pla
sta __save_a
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
990a704efdeee37df0c6ef379f434c297702c671
264
263
2021-08-23T04:12:10Z
71.198.117.145
0
Update interrupt sequences.
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm">
cld
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda __save_a
pha
lda __save_y
pha
JSR body
pla
sta __save_y
pla
sta __save_a
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
097f5379748c1b416ad818503f0d153d85f5688b
276
264
2021-09-23T16:27:28Z
2620:0:1001:7810:7BC3:5E92:F4AA:B122
0
Update Manual Interrupt Sequence for new Riscy calling convention
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm">
cld
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda mos8(__rc6)
pha
lda mos8(__rc7)
pha
lda mos8(__rc8)
pha
lda mos8(__rc9)
pha
lda mos8(__rc10)
pha
lda mos8(__rc11)
pha
lda mos8(__rc12)
pha
lda mos8(__rc13)
pha
lda mos8(__rc14)
pha
lda mos8(__rc15)
pha
lda mos8(__rc16)
pha
lda mos8(__rc17)
pha
lda mos8(__rc18)
pha
lda mos8(__rc19)
pha
lda mos8(__rc20)
pha
lda mos8(__rc21)
pha
lda mos8(__rc22)
pha
lda mos8(__rc23)
pha
lda mos8(__rc24)
pha
lda mos8(__rc25)
pha
lda mos8(__rc26)
pha
lda mos8(__rc27)
pha
lda mos8(__rc28)
pha
lda mos8(__rc29)
pha
lda __save_a
pha
lda __save_y
pha
JSR body
pla
sta __save_y
pla
sta __save_a
pla
sta mos8(__rc29)
pla
sta mos8(__rc28)
pla
sta mos8(__rc27)
pla
sta mos8(__rc26)
pla
sta mos8(__rc25)
pla
sta mos8(__rc24)
pla
sta mos8(__rc23)
pla
sta mos8(__rc22)
pla
sta mos8(__rc21)
pla
sta mos8(__rc20)
pla
sta mos8(__rc19)
pla
sta mos8(__rc18)
pla
sta mos8(__rc17)
pla
sta mos8(__rc16)
pla
sta mos8(__rc15)
pla
sta mos8(__rc14)
pla
sta mos8(__rc13)
pla
sta mos8(__rc12)
pla
sta mos8(__rc11)
pla
sta mos8(__rc10)
pla
sta mos8(__rc9)
pla
sta mos8(__rc8)
pla
sta mos8(__rc7)
pla
sta mos8(__rc6)
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
06784d92c7ff1cd79b1559c924ba15149e49114d
277
276
2021-09-24T22:26:17Z
2620:0:1001:7810:688D:2EB8:E151:A477
0
Remove __save_a and __save_y; these are now RC28 and RC29.
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is either written entirely in assembly.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm">
cld
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda mos8(__rc6)
pha
lda mos8(__rc7)
pha
lda mos8(__rc8)
pha
lda mos8(__rc9)
pha
lda mos8(__rc10)
pha
lda mos8(__rc11)
pha
lda mos8(__rc12)
pha
lda mos8(__rc13)
pha
lda mos8(__rc14)
pha
lda mos8(__rc15)
pha
lda mos8(__rc16)
pha
lda mos8(__rc17)
pha
lda mos8(__rc18)
pha
lda mos8(__rc19)
pha
lda mos8(__rc20)
pha
lda mos8(__rc21)
pha
lda mos8(__rc22)
pha
lda mos8(__rc23)
pha
lda mos8(__rc24)
pha
lda mos8(__rc25)
pha
lda mos8(__rc26)
pha
lda mos8(__rc27)
pha
lda mos8(__rc28)
pha
lda mos8(__rc29)
pha
JSR body
pla
sta mos8(__rc29)
pla
sta mos8(__rc28)
pla
sta mos8(__rc27)
pla
sta mos8(__rc26)
pla
sta mos8(__rc25)
pla
sta mos8(__rc24)
pla
sta mos8(__rc23)
pla
sta mos8(__rc22)
pla
sta mos8(__rc21)
pla
sta mos8(__rc20)
pla
sta mos8(__rc19)
pla
sta mos8(__rc18)
pla
sta mos8(__rc17)
pla
sta mos8(__rc16)
pla
sta mos8(__rc15)
pla
sta mos8(__rc14)
pla
sta mos8(__rc13)
pla
sta mos8(__rc12)
pla
sta mos8(__rc11)
pla
sta mos8(__rc10)
pla
sta mos8(__rc9)
pla
sta mos8(__rc8)
pla
sta mos8(__rc7)
pla
sta mos8(__rc6)
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
3fd5af4818937eebe9a049365531050072fb64a7
C calling convention
0
17
260
246
2021-08-15T05:03:26Z
71.198.117.145
0
Adopt the interrupt friendly calling convention across the board.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The caller-saved registers are flags, A, X, Y, RC2, RC3, RC4, and RC5. A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in caller-saved registers, except flags and Y. (It's not worth trying to get arguments into and out of the flags, and Y isn't used to make it easier to set up outgoing arguments for function calls.)
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
* Values may be returned on the soft stack if insufficiently many registers are available. Callers must reserve sufficient space for this as they do for arguments. The space reserved for arguments may overlap freely with the space used for return values; thus only enough space for the larger of the two need be allocated.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
361bb5d077a6d6b1e9c90be5f4fed489939a9566
268
260
2021-09-16T21:32:27Z
2620:0:1001:7810:66A0:16FD:D311:8D5E
0
Values can no longer be saved on the soft stack now that the number of required imaginary registers have increased.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The caller-saved registers are flags, A, X, Y, RC2, RC3, RC4, and RC5. A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in caller-saved registers, except flags and Y. (It's not worth trying to get arguments into and out of the flags, and Y isn't used to make it easier to set up outgoing arguments for function calls.)
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS0=(RC0, RC1) to RS128=(RC254, RC255)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
45777404870f288127bb06f5377252d6380dd9fb
275
268
2021-09-23T16:04:51Z
2A00:79E1:ABC:1328:543B:D492:5964:D581
0
Update calling convention
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The caller-saved registers are flags, A, X, Y, and RS1 to RS14 (RC1 to RC29). A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC1 to RC29. The other caller saved registers are not used for arguments, to make writing compiler thunks easier and to allow efficiently setting up call arguments.
* Pointers are preferentially assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC0, RC1) to RS7=(RC14, RC15)). If none are available, the low and high bytes are split and passed as above.
* If no registers remain available, any remaining bytes are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
cec82d73c041fa7f1402d8f5dd85b04614a3759a
286
275
2021-10-15T02:27:03Z
71.198.117.145
0
Update calling convention.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The caller-saved registers are flags, A, X, Y, and RS1 to RS14 (RC1 to RC29). A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC1 to RC29. The other caller saved registers are not used for arguments, to make writing compiler thunks easier and to allow efficiently setting up call arguments.
* Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC0, RC1) to RS7=(RC14, RC15)).
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
d3db27fdbe9fb362f1d9c41c5256b4bc2e4f3eff
287
286
2021-10-15T02:29:22Z
71.198.117.145
0
Correct calling convention.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* The caller-saved registers are flags, A, X, Y, and RS1 to RS14 (RC1 to RC29). A function may freely overwrite any of these, and the function's callers have to just deal with it. All other registers are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15. The other caller saved registers are not used for arguments, to make writing compiler thunks easier and to allow efficiently setting up call arguments.
* Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC1, RC2) to RS7=(RC14, RC15)).
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
0ae2ed2ce953967e39e63616c64b2efe4bdf6ccf
295
287
2021-11-16T07:40:46Z
71.198.117.145
0
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* A, X, Y, C, N, V, Z and RS1 to RS12 (RC2 to RC28) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS9 to RS15 (RC18 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC1, RC2) to RS7=(RC14, RC15)).
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. This is handled directly by Clang; LLVM itself should never see aggregates.
* Aggregate types are returned by a pointer passed as an implicit first argument. The resulting function returns void. This is handled directly by Clang; LLVM itself should never see aggregates.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
6b59dd5e63cd8629ee011166d8f6eb7f8ab4642f
Current status
0
25
261
238
2021-08-16T02:49:58Z
76.87.74.244
0
wikitext
text/x-wiki
== C compiler ==
The C compiler is LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available.
The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz.
Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
== Assembler ==
The assembler, llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The linker, lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the ELF format, for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
bb4eae10ac9c5e3c30bb4470d23391b9faf5d599
266
261
2021-08-30T16:25:45Z
Jbyrd
1
/* C compiler */
wikitext
text/x-wiki
== C compiler ==
The [[C compiler]] is LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available.
The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz.
Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
b93d2d405cea0a17d0a7b9c652ac3547d4b3d587
267
266
2021-08-30T16:26:49Z
Jbyrd
1
/* C compiler */
wikitext
text/x-wiki
== C compiler ==
The [[C compiler]] is based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available.
The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz.
Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
0b59d7a354bb0dcb144a6197d17aa5afdd618332
269
267
2021-09-18T19:53:29Z
Jbyrd
1
wikitext
text/x-wiki
== C compiler ==
The compiler is believed to be C99 compatible, but the quality of generated code is not yet at even a v0.1 level.
The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz.
The [[C compiler]] is based on LLVM's GlobalISel architecture. As of this writing it can pass the LLVM end-to-end test suite in a variety of optimization modes, although very little specific attention was paid to the quality of the generated output. It's still pretty OK, since we benefit greatly from LLVM's high-level optimizations, but there's still lots of low-level optimization opportunities available.
Although changes exist all over the LLVM source base, the backend for MOS architectures mainly exists in llvm/lib/Target/MC/MOS . Using the triple 'mos' will cause clang to use the new MOS backend. By default, this backend will target the 6502 as its default, which should work on all CPUs and NMOS implementations that claim 6502 compatibility.
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
16a408bc5a83c63b64c1112e1ed3cf5e96ebfd05
C compiler
0
18
265
240
2021-08-25T16:08:18Z
71.198.117.145
0
wikitext
text/x-wiki
A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
The Clang is broadly compatible with the freestanding portion of the C99 standard, with one big caveat:
* No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project.
Some of the GCC and Clang language extensions work, some don't. Similarly some of the C++ language features work, some don't. We haven't exhaustively catalogued this; there's a lot, and some are extremely obscure and nearly useless.
Still, some compiler extensions are de-facto standard, so we'll call out a few caveats for those.
Known Compiler Extension Caveats:
* The GCC/Clang alignment directives work only for global and static variables, not automatic (local) variables. Aligning the stack pointer is tricky, since it requires use of a frame pointer; but the rigors of 6502 indexing cause our frame pointer to be useless for this purpose. To get this to work, we may need *two* frame pointers. Expected call for this feature is low, so if you need it, let us know, and we'll reprioritize.
[[Category:C]]
9e33e649df37e1b2b73eaf1b81c36aad4f966e3a
Findings
0
43
270
2021-09-18T20:26:01Z
Jbyrd
1
Created page with "Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work. First, the assumption that a modern compiler framework, su..."
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
8bea2e5dce4c1b45094546912c901cbaa8c3cb6f
271
270
2021-09-18T20:27:17Z
Jbyrd
1
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. While this makes the job of the compiler author considerably easier, we like the challenge. Organizing code for the 6502 is actually rather difficult; making "a version of C" that is easy to compile just shifts the burden of this work back onto the user. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
9cf9bafda2ed6e975a65d9148cde109fcdc2e90e
278
271
2021-09-27T05:04:06Z
Jbyrd
1
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by our work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
9ea63ba6c04bd69793ff0b0770d45594ffcfede5
279
278
2021-09-27T05:04:27Z
Jbyrd
1
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
b2015a6b44bc02bb4c5c40db022a1d04b350539a
280
279
2021-10-08T15:25:00Z
2603:8000:3F44:8EAC:1811:8E3E:DD8D:BA82
0
Added missing word
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present these to LLVM as registers, which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
211c5d4acc965b4bff6a73dd88256b1f85ee5acc
281
280
2021-10-11T20:23:31Z
209.79.16.103
0
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
ef006a43fdcef9e38c335ab52b284276e6162bee
282
281
2021-10-11T20:26:24Z
209.79.16.103
0
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this; the analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code and interrupt-driven code, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
e047a19e3431977a7c41e2fe4821376788374b4b
283
282
2021-10-11T20:27:03Z
209.79.16.103
0
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates "as if there were stacks", without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code and interrupt-driven code, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
6350469981fb21663cf63c9056373cc3bacce3a5
284
283
2021-10-11T20:27:48Z
209.79.16.103
0
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code and interrupt-driven code, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
b012598de438fe5fbfcfcc94b33585b240746163
285
284
2021-10-11T20:29:06Z
209.79.16.103
0
wikitext
text/x-wiki
Several common assumptions about the MOS 6502 processor, and C compilers targeting it, are now refuted by the current work.
First, the assumption that a modern compiler framework, such as LLVM, cannot be targeted towards an old 8-bit CPU such as the 6502. LLVM's new GlobalISel architecture can very well be targeted to the 6502, and it can indeed produce superior code, if permitted to do so.
Second, the assumption that because the 6502 is "stackless," and has few registers, it is not a good host for C.
Regarding stacks, while it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
Regarding registers, the original 6502 designers were well aware of the 6502's register limitations, and so provided a bunch of zero-page addressing modes to compensate. We present ranges of zero page memory to LLVM in the form of ''imaginary registers'', which takes our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since 6502 instructions treat different zero page locations identically. While A, X, and Y are a bit unusual, they're not any worse than x86, and LLVM's register allocator is fully capable of handling them, even if the relationship is a bit strained.
Third, that "simpler is better" for producing a performant compiler for 8-bit targets. llvm-mos's architecture and design choices are not at all simple. I haven't counted, but I think llvm-mos is doing about 100 passes through the code, about 8 of which are specific to the MOS 6502.
Fourth, that the 6502 is implicitly some sort of "special" architecture, and it therefore requires special compilers, linkers, binary file formats, etc. We treat the 6502, ultimately, as just another target within the LLVM framework, and as such it benefits from all the industry-standard ELF-compatible file formats.
Fifth, that because the 6502 is a small target, it requires a smaller compiler and smaller tools. This assumption never really made sense anyway. In fact the opposite is true: if you want to do advanced codegen for the 6502, you need a really intelligent (and large) compiler and toolchain framework, not a small one. The state of the art of optimization has advanced leaps and bounds in the past three decades, and the poor old 6502 has received none of those benefits, until the current work.
Sixth, that peephole optimization produces the best codegen for the 6502. In fact, llvm-mos gets the most benefit out of 6502-specific optimizations relatively early in the LLVM machine function pass pipeline, and the code it produces (in small tests) is quite efficient, even without any 6502-specific peephole optimizer at all. "The more clothes you put on during the day, the more you have to take off at night." One high-level instruction can become a big block of 6502, so a single high-level optimization that removes it can prevent a thousand cases from being needed to handle it later.
Seventh, that because the 6502 is small, it requires some sort of specialized language (in the [https://dwheeler.com/6502/ David A. Wheeler] sense) in order to generate performant code. As it turns out, ''anything that generates LLVM IR,'' can be a candidate for running on the 65xx series of processors. Making "a version of C" that is easy to compile just shifts the burden of writing a modern compiler, back onto the user, in the form of limited functionality. LLVM will likely be able to handle compiling other languages to the 6502, at some point in the future. Rust support has already been proven, but there are no problems in principle with lowering many more languages to the 6502.
[[Category:Main]]
49bc9b85ac6f0277f3f1908625f8cbbfd3922f8a
288
285
2021-10-17T04:58:39Z
71.198.117.145
0
Update findings page.
wikitext
text/x-wiki
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
== Stacks ==
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
== Registers ==
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
[[Category:Main]]
2171799d6474f9cbc969204e79e4c7d521b1d3cf
289
288
2021-10-19T20:23:36Z
2620:15C:2D1:203:4B9E:24FF:290D:9B8C
0
Mention other non-codegen advantages of LLVM
wikitext
text/x-wiki
GEfficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
== Stacks ==
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
== Registers ==
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
=== ELF ===
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
=== GAS Assembler ===
The assembler's directives are broadly compatible GNU Assembler; as such we have access to all the features that come with it. Weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang; since it uses the underlying assembler to interpret these fragments.
=== Non-C languages ===
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
[[Category:Main]]
2b2f38faf9715b51eceeff965ba67d28f48d2801
290
289
2021-10-25T18:28:27Z
107.195.41.193
0
wikitext
text/x-wiki
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
== Stacks ==
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
== Registers ==
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
=== ELF ===
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
=== GAS Assembler ===
The assembler's directives are broadly compatible GNU Assembler; as such we have access to all the features that come with it. Weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang; since it uses the underlying assembler to interpret these fragments.
=== Non-C languages ===
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
[[Category:Main]]
147abe7d0eab985a0eb4f6baa21cd3a62f0859e2
291
290
2021-10-25T18:30:07Z
107.195.41.193
0
/* GAS Assembler */
wikitext
text/x-wiki
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
== Stacks ==
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
== Registers ==
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
=== ELF ===
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
=== GAS Assembler ===
The assembler's directives are broadly compatible with GNU Assembler; as such we have access to all the features that come with it. Weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments.
=== Non-C languages ===
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
[[Category:Main]]
bf34f793e29744f3267196bd24a2da7b9e590775
292
291
2021-10-25T18:37:10Z
107.195.41.193
0
/* GAS Assembler */
wikitext
text/x-wiki
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
== Stacks ==
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
== Registers ==
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
=== ELF ===
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
=== GAS Assembler ===
The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments.
=== Non-C languages ===
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
[[Category:Main]]
3f4a2a2f5b7b9e7f2536ca2a11a1f159b3af05b4
293
292
2021-10-25T18:40:28Z
107.195.41.193
0
wikitext
text/x-wiki
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
=== Stacks ===
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behavior in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
=== Registers ===
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== ELF ==
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
== GAS Assembler ==
The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments.
== Non-C languages ==
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
[[Category:Main]]
396854bfd5ab0ec7efd1c7fce94d94ded3b6aea6
Rationale
0
26
272
243
2021-09-18T20:28:06Z
Jbyrd
1
/* Findings */
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program.
At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The number and placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers.
Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow).
As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
See also [[Findings]].
[[Category:Main]]
f048fc775241c31c9ddb0dd3b16b0ca74fd91c4c
303
272
2022-02-24T23:01:59Z
2620:15C:2D1:203:B7B0:54A:7A64:FBA5
0
Removed number of zp regs customizabiilty.
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program.
At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers.
Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow).
As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
A proof of concept exists, demonstrating that [http://forum.6502.org/viewtopic.php?f=2&t=6450&p=84195#p84048 llvm-mos can support Rust as a source language]. This suggests that LLVM-MOS can support other source languages as well, such as C++.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
See also [[Findings]].
[[Category:Main]]
b5e2cfbc99da4b1a895785c03af53e9ef77c4386
Assembler
0
13
297
162
2022-01-17T20:24:16Z
Jackoalan
5
mos_addr_asciz
wikitext
text/x-wiki
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
=== Target-specific directives ===
{| class="wikitable"
|+
!Syntax
!Description
|-
| .mos_addr_asciz <expression>, <num-digit-chars>
|Emits a fixed-length decimal ASCII string of a relocatable expression. Primary use case is for generating the BASIC header on C64.
|}
[[Category:Assembly]]
0c1b7ab42308dd414505c14912e2525280c545ed
Getting started
0
23
300
234
2022-01-27T04:38:40Z
71.198.117.145
0
Soften the warning; we're getting closer.
wikitext
text/x-wiki
NOTICE: There's still much work to be optimization work to be done before our first official release. Please don't publicly review, compare, or benchmark against other compilers at this.
To keep this project a clean fork of LLVM, no target-specific source code or libraries are part of this project. These are contained in the related llvm-mos-sdk. The default mos target will only use compiler built-in include and library paths (e.g., stdint.h), so the compiler can technically be used without the SDK; however, this means that you will have to provide your own libc and your own run-time initialization. If you don't understand what this means, then you should use llvm-mos in conjunction with the llvm-mos-sdk.
For more information about this project, please see llvm-mos.org.
For information about the current status of this project, please see [[Current status|Current status.]]
To learn why this project exists, please see [[Rationale]].
= Getting started =
== Download the LLVM-MOS tools ==
If you want to play with the current state of the LLVM-MOS toolchain, you may not have to build LLVM-MOS from source code yourself. Instead, just download the most recent binaries for your platform:
* [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-darwin-main MacOS]
* [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-linux-main Linux]
* [https://github.com/llvm-mos/llvm-mos/releases/tag/llvm-mos-windows-main Windows]
These binaries are built from the main branch of the LLVM-MOS project, using Github's actions functionality.
== Or, build the LLVM-MOS tools ==
However, if you're allergic to precompiled binaries, or your platform is not listed above, then you'll need to compile LLVM-MOS for your own platform.
Generally, compiling LLVM-MOS follows the same convention as compiling LLVM. First, please review the hardware and software requirements for building LLVM.
Once you meet those requirements, you may use the following formula within your build environment:
=== Clone the LLVM-MOS repository ===
On Linux and MacOS:
<code>git clone <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
On Windows:
<code>git clone --config core.autocrlf=false <nowiki>https://github.com/llvm-mos/llvm-mos.git</nowiki></code>
If you fail to use the --config flag as above, then verification tests will fail on Windows.
=== Configure the LLVM-MOS project ===
<code>cd llvm-mos
cmake -C clang/cmake/caches/MOS.cmake [-G <generator>] -S llvm -B build [...]</code>
This configuration command seeds the CMake cache with values from MOS.cmake. Feel free to review and adjust these values for your environment.
Additional options can be added to the cmake command, which override the values provided in MOS.cmake. A handful are listed below. For a complete list of options, see Building LLVM with CMake.
* <code>-G <generator></code> --- Lets you choose the CMake generator for your build environment. CMake will try to automatically detect your build tools and use them; however, it's recommended to install Ninja and pass Ninja as the parameter to the -G command.
* <code>-DLLVM_ENABLE_PROJECTS=...</code> --- semicolon-separated list of the LLVM sub-projects you'd like to additionally build. Can include any of: clang, clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld, polly, or debuginfo-tests.
* <code>-DCMAKE_INSTALL_PREFIX=directory</code> --- Specify for ''directory'' the full path name of where you want the LLVM tools and libraries to be installed (default <code>/usr/local</code>).
* <code>-DCMAKE_BUILD_TYPE=type</code> --- Valid options for ''type'' are Debug, Release, RelWithDebInfo, and MinSizeRel. Default is MinSizeRel, if you are using the MOS.cmake cache file.
* <code>-DLLVM_ENABLE_ASSERTIONS=On</code> --- Compile with assertion checks enabled (default is Yes for Debug builds, No for all other build types).
=== Build the LLVM-MOS project ===
<code>cmake --build build [-- [options] <target>]</code>
The default target will build all of LLVM. The <code>check-all</code> target will run the regression tests. The <code>distribution</code> target will build a collection of all the LLVM-MOS tools, suitable for redistribution.
CMake will generate targets for each tool and library, and most LLVM sub-projects generate their own <code>check-<project></code> target.
Running a serial build will be slow. To improve speed, try running a parallel build. That's done by default in Ninja; for <code>make</code>, use the option <code>-j NNN</code>, where <code>NNN</code> is the number of parallel jobs, e.g. the number of CPUs you have.
= Help us out =
We need your help! Please review the issue tracker, please review the current state of the code, and jump in and help us with pull requests for bug fixes.
All LLVM-MOS code is expected to ''strictly'' observe the LLVM coding standards. That means your code must have been run through clang-format with the --style set to LLVM, and clang-tidy with the LLVM coding conventions with the llvm-*, modernize-*, and cppcore-* checks enabled. If your code does not observe these standards, there's a good chance we'll reject it, unless you have a ''good reason'' for not observing these rules.
If you add new functionality or an optimization pass to LLVM-MOS, we're not going to accept it unless you have modified the associated test suite to exercise your new functionality. Drive-by feature pulls will probably not be accepted, unless their new functionality is too trivial to be tested. GlobalISel gives you no excuses ''not'' to write a full test suite for your codegen pass or your new functionality.
You can submit well-written, carefully researched issue requests via the issue tracker. Please note, we don't have the bandwidth yet to handle "why dosent my pogrem compil" type requests.
Additionally, the current state of our documentation at [[Welcome|https://llvm-mos.org]] can always use improvements and clarifications.
[[Category:Main]]
d326aba630a5223500ee9fbb1504f79454650f23
305
300
2022-02-25T18:30:39Z
Mysterymath
3
One source of truth for getting started.
wikitext
text/x-wiki
See [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting Stated with the SDK].
[[Category:Main]]
65e596c6df8aed2c2342943ff3664219b308154f
C volatile
0
44
301
2022-02-18T02:14:55Z
71.198.117.145
0
Add C volatile page.
wikitext
text/x-wiki
The compiler will freely issue indexed addressing modes wherever appropriate. Due to a processor bug, these addressing modes can cause NMOS 6502 to issue a spurious read to a memory location one page lower than the one intended. These reads are not considered to constitute an access to a volatile object, even if they overlap with one in memory.
To avoid accidentally triggering I/O, pointer arithmetic should be avoided for addresses one page above read-sensitive I/O locations. To be safe, such routines can be written in assembly.
The read/modify/write (RMW) instructions (e.g., INC) also issue a spurious read or write to the location, depending on the 6502 version. If the underlying object is volatile, these accesses would be considered to be volatile accesses; accordingly, the compiler avoids generating RMW instructions for such addresses. Instead, the compiler will manually issue the load, manipulate the value, and store it back.
544f3a6e001e8620c473dc853087ed4a76cd4c10
302
301
2022-02-18T02:15:33Z
71.198.117.145
0
Add category.
wikitext
text/x-wiki
The compiler will freely issue indexed addressing modes wherever appropriate. Due to a processor bug, these addressing modes can cause NMOS 6502 to issue a spurious read to a memory location one page lower than the one intended. These reads are not considered to constitute an access to a volatile object, even if they overlap with one in memory.
To avoid accidentally triggering I/O, pointer arithmetic should be avoided for addresses one page above read-sensitive I/O locations. To be safe, such routines can be written in assembly.
The read/modify/write (RMW) instructions (e.g., INC) also issue a spurious read or write to the location, depending on the 6502 version. If the underlying object is volatile, these accesses would be considered to be volatile accesses; accordingly, the compiler avoids generating RMW instructions for such addresses. Instead, the compiler will manually issue the load, manipulate the value, and store it back.
[[Category:C]]
a65e42a03ddaafb1dba8789791728594b4c2a8e9
Current status
0
25
306
269
2022-02-25T18:34:08Z
Mysterymath
3
Update status.
wikitext
text/x-wiki
== C compiler ==
The compiler is broadly freestanding C99/C++ compatible (notably excepting float/double and C++ exceptions). It emits reasonably good code.
The LLVM SingleSource end-to-end test cases pass on a simulated 6502. This is true at -O0, -O3, -Os, and -Oz.
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
1a1d11f6c78c4c34ce320ac5df63f9774f80dfd1
307
306
2022-02-25T18:38:21Z
Mysterymath
3
Add in info from first release.
wikitext
text/x-wiki
== C compiler ==
* Code generation for the Commodore 64, Atari 800, and an included 6502 simulator.
* The high and low-level optimizations expected of a young-ish LLVM backend
** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers
** The imaginary registers can be placed anywhere and need not be contiguous.
** The calling convention passes through registers whenever possible.
** Loop optimizations to select 6502 addressing modes
** Whole program "static stack" optimization
*** Automatically identifies non-reentrant functions and allocates their frames as static globals
*** Programs without recursion or complex function pointers may not need a soft stack at all.
*** No manual annotations required
** Link time inlining and optimization across the whole program
*** Includes SDK libraries. Library calls can be often optimized away completely!
* Broad C99 and C++11 freestanding standards compatibility
** Interrupt handling
** C++ templates
** C++ virtual functions
** C++ new/delete
** C++ Run-Time Type Information (dynamic_cast, typeid)
** C++ static constructors/destructors (run before and after main)
** C++ "magic" function local static constructors/destructors
* Excellent compiler usability
** Clang's world-class error messages
** IDE integration through the included custom clangd's Language Server Protocol
** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code>
* A small standard library sufficient to provide the above and a few extras
** Simple printf
** Simple malloc/free
** exit, _Exit, and atexit
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
ddd78caee471fc9a4df498b096eca21b799a199c
308
307
2022-02-25T18:40:27Z
Mysterymath
3
Add exceptions
wikitext
text/x-wiki
== C compiler ==
* Code generation for the Commodore 64, Atari 800, and an included 6502 simulator.
* The high and low-level optimizations expected of a young-ish LLVM backend
** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers
** The imaginary registers can be placed anywhere and need not be contiguous.
** The calling convention passes through registers whenever possible.
** Loop optimizations to select 6502 addressing modes
** Whole program "static stack" optimization
*** Automatically identifies non-reentrant functions and allocates their frames as static globals
*** Programs without recursion or complex function pointers may not need a soft stack at all.
*** No manual annotations required
** Link time inlining and optimization across the whole program
*** Includes SDK libraries. Library calls can be often optimized away completely!
* Broad C99 and C++11 freestanding standards compatibility
** Interrupt handling
** C++ templates
** C++ virtual functions
** C++ new/delete
** C++ Run-Time Type Information (dynamic_cast, typeid)
** C++ static constructors/destructors (run before and after main)
** C++ "magic" function local static constructors/destructors
* Excellent compiler usability
** Clang's world-class error messages
** IDE integration through the included custom clangd's Language Server Protocol
** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code>
* A small standard library sufficient to provide the above and a few extras
** Simple printf
** Simple malloc/free
** exit, _Exit, and atexit
=== Notable Exceptions ===
* C99 requires supporting 64KiB locals
* Hosted C with all the standard library bells and whistles.
* Float/double
* C++ Exceptions
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
bb2050d0834bd002e2ced3096562adc9902932b5
C calling convention
0
17
309
295
2022-03-08T06:11:09Z
71.198.117.145
0
Update calling convention to make small aggregates passed by value.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* A, X, Y, C, N, V, Z and RS1 to RS12 (RC2 to RC28) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS9 to RS15 (RC18 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are assigned to imaginary register pairs, functioning as pointer registers (i.e., RS1=(RC1, RC2) to RS7=(RC14, RC15)).
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
43fbc9dc3b6b5596100935b793effe22979fc171
326
309
2022-05-18T08:13:30Z
167.98.40.6
0
Correct the calling convention description.
wikitext
text/x-wiki
The current calling convention is somewhat simplistic; it will be tuned for performance and size before the initial release of the compiler.
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
98aa04c89b3521639998adf72b4f2b2d9e0b908e
Linker Script
0
45
310
2022-03-19T20:05:10Z
Mysterymath
3
Add linker script description.
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts.
See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior.
This page describes LLVM-MOS's extensions of LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>
* A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code>
Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size.
Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the section are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that the output section doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros.
<code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region.
The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
d5eed62a81a647f6079e89985035d2d38c7c5f6f
311
310
2022-03-19T20:07:44Z
Mysterymath
3
Add to category.
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts.
See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior.
This page describes LLVM-MOS's extensions of LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>
* A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code>
Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size.
Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the section are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that the output section doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros.
<code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region.
The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
[[Category:Linking]]
b9008199cd41b5bfb1f82b55ae44b795f62f8b5a
312
311
2022-03-21T01:26:06Z
71.198.117.145
0
Clarify section vs region.
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts.
See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior.
This page describes LLVM-MOS's extensions of LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>
* A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code>
Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size.
Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros.
<code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region.
The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
[[Category:Linking]]
0cfa07ba332911eb0b31d8fd473e314a8b6bdd48
Welcome
0
1
313
304
2022-04-15T02:15:43Z
71.198.117.145
0
Add NES mention
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
== Welcome to the llvm-mos project! ==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
7a9418d53c3ddb0bc6f151d7e553ee2301723adb
315
313
2022-04-19T18:01:12Z
Jbyrd
1
Added OSC1P
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
== Welcome to the llvm-mos project! ==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
==== Notice ====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
=== Category tree ===
<CategoryTree mode="pages" depth="3" hideroot="on">Main</CategoryTree>
=== Categories ===
{{Special:AllPages|namespace=14}}
=== Pages ===
{{Special:AllPages}}
c8d22227c9e46d1e57c772712b645a0ca61f7b86
336
315
2022-07-06T17:37:18Z
Jbyrd
1
Added Steven Poulton's NES project
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
==Welcome to the llvm-mos project!==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK.
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
6a615b90fdf3bfac0dbb51c07f52d78b5f62af57
350
336
2022-08-02T14:45:21Z
Mysterymath
3
Add SDK doxygen link.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
==Welcome to the llvm-mos project!==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
f3ca94eac06b8b4dd7b8379b584bbf4069b62a1b
354
350
2022-08-12T06:12:02Z
Wombat
9
Add Commander X16 screenshot
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]NES project in pure C99 using llvm-mos, by Steven Poulton]]
==Welcome to the llvm-mos project!==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
997002fc7e71f70f78fe507010b4b8bae8e5beee
355
354
2022-08-12T06:18:45Z
Wombat
9
Move Commander X16
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://join.slack.com/t/llvm-mos/shared_invite/zt-t88fyh4i-EGCLe~MSlHdz3~h~yYHgFA please join our Slack group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
2010dcbec75d858e1ba0cd93a0025100bad2bc14
File:Llvm-mos hello world osi c1p.png
6
46
314
2022-04-19T17:59:42Z
Jbyrd
1
wikitext
text/x-wiki
Hello world in C, targeting Ohio Scientific Challenger 1P
9a86dbf8ae9e8cc93f9eb6bd4f9f29857bcfbbd4
Porting
0
47
316
2022-04-23T02:17:26Z
71.198.117.145
0
Start working on porting guide; not yet finished, but don't want to lose changes.
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
Let's try again!<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>
50ebc990e4ffd635df72f9eda7b5069239028aff
317
316
2022-04-23T02:34:51Z
Mysterymath
3
Add ELF information.
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
Let's try again!<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>Okay, so that compiled! Unfortunately, it's an ELF file, which isn't at all what the target takes.
LLVM-MOS uses ELF for its object files, libraries, and executables (by default). This is because of the rich information about the contents provided by this format; this in turn allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
30ba63f5a01c3360433dfd65d99bd563abf4a52a
318
317
2022-04-23T02:50:11Z
Mysterymath
3
/* Output Format */
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>.
Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>.
== Compiling: Third Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
main.elf
...
$ file main
main:data
$ file main.elf
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
$ hexdump -C main
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 02 |..|
00010002
</syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script.
Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>.
f01af42c583a9402a133527ad8c4e7b656807263
319
318
2022-04-24T00:59:38Z
Mysterymath
3
Optional Libraries
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>.
Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>.
== Compiling: Third Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
main.elf
...
$ file main
main:data
$ file main.elf
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
$ hexdump -C main
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 02 |..|
00010002
</syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script.
Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>.
== Optional Libraries ==
Take another look at the disassembly:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land.
This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors).
Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target.
The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target].
One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities:
{| class="wikitable"
!Exit Library
!Description
|-
|<code>exit-custom</code>
|Run some custom code (e.g., execute a special BRK, which goes into an underlying OS).
|-
|<code>exit-loop</code>
|Go into an infinite loop.
|-
|<code>exit-return</code>
|Return from <code>_start</code>
|}
Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>.
There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you.
We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Nothing changed! Is there a problem? Nope!
Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.
82e5466f72689d6516094af0ba4d4ffa897763d2
320
319
2022-04-24T01:07:17Z
Mysterymath
3
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>.
Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>.
== Compiling: Third Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
main.elf
...
$ file main
main:data
$ file main.elf
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
$ hexdump -C main
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 02 |..|
00010002
</syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script.
Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>.
== Optional Libraries ==
Take another look at the disassembly:<syntaxhighlight lang="shell-session">
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land.
This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors).
Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target.
The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target].
One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities:
{| class="wikitable"
!Exit Library
!Description
|-
|<code>exit-custom</code>
|Run some custom code (e.g., execute a special BRK, which goes into an underlying OS).
|-
|<code>exit-loop</code>
|Go into an infinite loop.
|-
|<code>exit-return</code>
|Return from <code>_start</code>
|}
Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>.
There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you.
We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Nothing changed! Is there a problem? Nope!
Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.
0ce1b107c9511e89975ba5774c2c258af4abc29a
324
320
2022-04-28T17:31:21Z
Mysterymath
3
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>.
Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>.
== Compiling: Third Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
main.elf
...
$ file main
main:data
$ file main.elf
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
$ hexdump -C main
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 02 |..|
00010002
</syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script.
Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>.
== Optional Libraries ==
Take another look at the disassembly:<syntaxhighlight lang="shell-session">
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land.
This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors).
Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target.
The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target].
One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities:
{| class="wikitable"
!Exit Library
!Description
|-
|<code>exit-custom</code>
|Run some custom code (e.g., execute a special BRK, which goes into an underlying OS).
|-
|<code>exit-loop</code>
|Go into an infinite loop.
|-
|<code>exit-return</code>
|Return from <code>_start</code>
|}
Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>.
There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you.
We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Nothing changed! Is there a problem? Nope!
Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.'
= Extending the SDK =
After porting LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated.
To do so, continue onward to the [[Extending SDK]] guide.
0f75cac25450f8b096c5e81f23abaa2b54d5a099
331
324
2022-06-15T04:23:06Z
Pyrex8
8
; need at end of line otherwise: ld.lld: error: link.ld:15: ; expected, but got OUTPUT_FORMAT >>> OUTPUT_FORMAT { FULL(ram) SHORT(_start) } >>> ^
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY { ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00 }
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>.
Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>.
== Compiling: Third Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
main.elf
...
$ file main
main:data
$ file main.elf
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
$ hexdump -C main
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 02 |..|
00010002
</syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script.
Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>.
== Optional Libraries ==
Take another look at the disassembly:<syntaxhighlight lang="shell-session">
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land.
This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors).
Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target.
The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target].
One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities:
{| class="wikitable"
!Exit Library
!Description
|-
|<code>exit-custom</code>
|Run some custom code (e.g., execute a special BRK, which goes into an underlying OS).
|-
|<code>exit-loop</code>
|Go into an infinite loop.
|-
|<code>exit-return</code>
|Return from <code>_start</code>
|}
Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>.
There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you.
We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0;
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Nothing changed! Is there a problem? Nope!
Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.'
= Extending the SDK =
After porting LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated.
To do so, continue onward to the [[Extending SDK]] guide.
ab2b3386b992e432fc58053067aeea1393cce5a0
Extending SDK
0
48
321
2022-04-27T23:28:41Z
Mysterymath
3
Start SDK upstreaming guide.
wikitext
text/x-wiki
After [[Porting]] LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated.
This guide follows the Porting Guide above; make sure you've at least skimmed it before continuing, otherwise this guide likely won't make as much sense.
bd3d573a1f3631b2b1536d8528e7cda576c688ad
322
321
2022-04-27T23:36:15Z
Mysterymath
3
wikitext
text/x-wiki
After [[Porting]] LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated.
This guide follows the Porting Guide above; make sure you've at least skimmed it before continuing, otherwise this guide likely won't make as much sense.
== Review ==
Completing the porting guide produced the following artifacts:
A linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>A command line to build a binary:
<code>mos-common-clang -o main -Os main.c -lexit-loop -linit-stack</code>
This is enough to set up a freestanding platform in the SDK.
== CMake ==
As mentioned previously, our SDK uses CMake to build. If you haven't done so already, check out and build the [https://github.com/llvm-mos/llvm-mos-sdk SDK sources] using CMake.
Let's call our platform <code>xyzzy</code>, for old times' sake. Make a <code>xyzzy</code> subdirectory under the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform mos-platform] directory for the platform to live in. Also, create an empty <code>CMakeLists.txt</code> file in that directory; that will eventually tell CMake how to build the platform.
Additionally, tell CMake that the new directory exists by adding <code>add_subdirectory(xyzzy)</code> to [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/CMakeLists.txt mos-platform/CMakeLists.txt].
== Platform Definition ==
2edc5f5fba9dc69b2122af435e7a01fdf671a111
323
322
2022-04-28T17:29:41Z
Mysterymath
3
Finish.
wikitext
text/x-wiki
After [[Porting]] LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated.
This guide follows the Porting Guide above; make sure you've at least skimmed it before continuing, otherwise this guide likely won't make as much sense.
== Review ==
Completing the porting guide produced the following artifacts:
A linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>A command line to build a binary:
<code>mos-common-clang -o main -Os main.c -lexit-loop -linit-stack</code>
This is enough to set up a freestanding platform in the SDK.
== CMake ==
As mentioned previously, our SDK uses CMake to build. If you haven't done so already, check out and build the [https://github.com/llvm-mos/llvm-mos-sdk SDK sources] using CMake.
Let's call our platform <code>xyzzy</code>, for old times' sake. Make a <code>xyzzy</code> subdirectory under the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform mos-platform] directory for the platform to live in. Tell CMake that the new directory exists by adding <code>add_subdirectory(xyzzy)</code> to [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/CMakeLists.txt mos-platform/CMakeLists.txt]. This will cause CMake to look for a <code>CMakeLists.txt</code> file in the <code>xyzzy</code> subdirectory, which will tell it how to build the <code>xyzzy</code> platform.
== Platform Definition ==
A minimal platform <code>CMakeLists.txt</code> for <code>xyzzy</code> is just <code>platform(xyzzy)</code>. Building and installing the SDK afterwards will then produce a <code>mos-xyzzy-clang</code> symlink that tells Clang to use the platform.
This produces an incomplete target with nothing in it. Our target should be complete though; it should be possible to produce binaries from it. So we can tell CMake that by using <code>platform(xyzzy COMPLETE)</code>.
This will cause CMake to link for a <code>link.ld</code> file in the <code>xyzzy</code> subdirectory. Placing the <code>link.ld</code> file there allows the build and install to succeed and causes <code>mos-xyzzy-clang</code> to automatically use that linker script.
This still doesn't allow producing binaries though; the command line used for this was <code>mos-common-clang</code>, not <code>mos-clang</code>. Since the <code>xyzzy</code> platform is based off of the <code>common</code> platform, we need to change the platform definition to <code>platform(xyzzy COMPLETE PARENT common)</code>. With this, <code>mos-xyzzy-clang</code> can produce executables.
== Platform Libraries ==
There's one remaining part of the command line to deal with: <code>-lexit-loop and -linit-stack</code>. These libraries need to be included in the platform libraries for <code>xyzzy</code>.
For each platform, the Clang driver expects the following files to be available to the linker:
{| class="wikitable"
!File
!Description
|-
|<code>link.ld</code>
|Linker Script
|-
|<code>crt0.o</code>
|Mandatory entry/exit functionality
|-
|<code>libcrt0.a</code>
|Optional entry/exit functionality
|-
|<code>libcrt.a</code>
|C runtime
|-
|<code>libc.a</code>
|C standard library
|}
<code>link.ld</code> has already been discussed. <code>crt0.o</code> and <code>libcrt0.a</code> contain routines used at the beginning and end of a program. <code>libcrt0.a</code> contains functionality that may or may not be included, depending on what symbols are referenced in the program, while <code>crt0.o</code> contains the essential scaffolding to start and end a program. <code>libcrt.a</code> contains routines used to implement compiler functionality like multiplication and division, while <code>libc.a</code> contains the C standard library and support for some C++ language features.
We can't just include <code>-lexit-loop</code> and <code>-linit-stack</code> on the command line; the above files are structured so that compiler flags like <code>-nostartfiles</code> do what they logically should. So, we need to include <code>libexit-loop.a</code> and <code>libinit-stack.a</code> into the appropriate platform library, <code>libcrt0.a</code>.
To do so, we add to the <code>xyzzy</code> <code>CMakeLists.txt</code> :<syntaxhighlight lang="cmake">
platform(xyzzy COMPLETE PARENT common)
if(NOT CMAKE_CROSSCOMPILING)
return()
endif()
add_platform_library(xyzzy-crt0)
merge_libraries(xyzzy-crt0
common-crt0
common-init-stack
common-exit-loop
)
</syntaxhighlight>The early return just makes sure the following libraries aren't compiled for the host; they're 6502 code, so they need to be compiled with llvm-mos.
The remainder of the code creates a <code>libcrt0.a</code> for <code>xyzzy</code> that bundles in the <code>libcrt0.a</code>, <code>libinit-stack.a</code> , and <code>libexit-loop.a</code> libraries from the <code>common</code> platform. The other libraries bleed through from the <code>common</code> platform, while the <code>libcrt0.a</code> for <code>xyzzy</code> will be used in preference to the one from <code>libcommon</code>
With this, <code>mos-xyzzy-clang</code> will behave like to the original command line, and the platform is now fully incorporated into the SDK.
495d82a6a334e42aad5a045cf9e858d1e3dc7055
Modifiers
0
16
325
169
2022-04-29T21:47:36Z
71.198.117.145
0
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar).
[[Category:Assembly]]
a206651555e4666ba44d36329fcb347e7cd62ae9
Frequently asked questions
0
49
327
2022-05-18T08:40:08Z
167.98.40.6
0
Add a FAQ page.
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
2d03cb607ce2149dc690a039c2543ed05d4a6b57
333
327
2022-06-28T04:07:22Z
71.198.117.145
0
Add FAQ entry for missing sections.
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
842194b19c2291c8684577effc56c146aaddb027
337
333
2022-07-13T04:48:25Z
Mysterymath
3
Add a guide for dealing with inline assembly to the FAQ.
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char a) {
asm ("some compiler-unknown string of inline assembly goes here");
return a;
}
</syntaxhighlight>The calling convention has the value <code>a</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char a) {
asm ("some compiler-unknown string of inline assembly goes here"::"a");
return a;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void bar(char a);
void foo(char a) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>a</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>a</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void bar(char a);
void foo(char a) {
asm volatile ("st%0 0x1234"::"R"(a));
}
</syntaxhighlight>The <code>R</code> constraint on the <code>a</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes.
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
155fa404d653a68941c2de03825c08e4c441989c
338
337
2022-07-13T04:52:47Z
Mysterymath
3
Move to Main category.
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char a) {
asm ("some compiler-unknown string of inline assembly goes here");
return a;
}
</syntaxhighlight>The calling convention has the value <code>a</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char a) {
asm ("some compiler-unknown string of inline assembly goes here"::"a");
return a;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void bar(char a);
void foo(char a) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>a</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>a</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void bar(char a);
void foo(char a) {
asm volatile ("st%0 0x1234"::"R"(a));
}
</syntaxhighlight>The <code>R</code> constraint on the <code>a</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes.
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
[[Category:Main]]
7f5b19a45886721a265d31efd6ba3d90b42c6737
341
338
2022-07-19T05:01:32Z
Mysterymath
3
Fix inline assembly FAQ
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here"::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>a</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>a</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234"::"R"(value));
}
</syntaxhighlight>The <code>R</code> constraint on the <code>a</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes.
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
[[Category:Main]]
9d9fdd84575c641e6e6da8114a1bf34ce21c6f77
342
341
2022-07-19T07:57:38Z
Wombat
9
Changed label "a" to "value". Added extra ":" before clobber.
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234"::"R"(value));
}
</syntaxhighlight>The <code>R</code> constraint on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes.
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
[[Category:Main]]
8025f097a4b41ec4acfccb10920ed36db637db0d
Code generation overview
0
22
328
228
2022-06-07T17:05:24Z
Jbyrd
1
Added YouTube talk
wikitext
text/x-wiki
See also [https://www.youtube.com/watch?v=2lW3WHPtmKo this talk at 2022 EuroLLVM], which gives a high-level overview of llvm-mos's code generation strategy.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a small handful of 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and am 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
[[Category:Code generation]]
458e4a9c5460dd8f716eed188e894897b9a6115d
Assembler relaxation
0
19
329
167
2022-06-08T04:42:22Z
Stm
7
Fixed typo: The special section name for the zero page is ".zp" and not "zp"
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section .zp
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x</code>
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"z",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
fcae9575990ea1d661757a1bd4140eadee8ce438
330
329
2022-06-09T21:02:42Z
Mysterymath
3
Add allocatable flags to zp example.
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section .zp,"a",@nobits
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"az",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
c0f4143c142143dcc75841ccb4457e89057fef98
Rust
0
50
332
2022-06-26T17:50:44Z
Mrk
6
Created blank page
wikitext
text/x-wiki
da39a3ee5e6b4b0d3255bfef95601890afd80709
343
332
2022-07-19T08:54:23Z
Wombat
9
Added the first Rust information
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost).
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE.
=== Other resources ===
* For commodore targets like c64 and mega65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust">
use mos_hardware::{c64};
let old_border_color = (*c64::VIC).border_color.read();
</syntaxhighlight>
431ae92fc6d5ddbd3b01ee5d99d4b1f156f46807
344
343
2022-07-19T09:04:30Z
Wombat
9
Capital letters
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost).
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE.
=== Other resources ===
* For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust">
use mos_hardware::{c64};
let old_border_color = (*c64::VIC).border_color.read();
</syntaxhighlight>
d4267dd98b81f2127d72e6f19293cbdb81f73683
345
344
2022-07-19T09:08:31Z
Wombat
9
Update example
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost).
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE.
=== Other resources ===
* For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust">
use mos_hardware::{c64, vic2}
(*c64::VIC).border_color.write(vic2::LIGHT_RED);
</syntaxhighlight>
e12a8307fa0ab5abdc26fb834ce8975bfa5a785d
346
345
2022-07-19T09:13:34Z
Wombat
9
/* Other resources */ Add examples
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost).
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE.
=== Other resources ===
* For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust">
use mos_hardware::{c64, vic2}
(*c64::VIC).border_color.write(vic2::LIGHT_RED);
</syntaxhighlight> A set of [https://github.com/mlund/mos-hardware/tree/main/examples code examples are available] (plasma effect, raster interrupt)
3a729b88887e08abc161d2a5d41b4f8b784df98d
347
346
2022-07-19T10:46:02Z
Wombat
9
Added atari utility crate
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/tmp:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where host files in <code>$HOME/tmp</code> are mounted in <code>/hostfiles</code>. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for the x86 architecture. Once exited from the shell, you may return to the container at any time (data will not be lost).
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from within the Visual Code Studio IDE.
=== Other resources ===
* Utility [https://github.com/mrk-its/a800xl-utils crate for for Atari 800XL].
* For Commodore targets like C64 and MEGA65, there's a [https://crates.io/crates/mos-hardware MOS Hardware crate] that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust">
use mos_hardware::{c64, vic2}
(*c64::VIC).border_color.write(vic2::LIGHT_RED);
</syntaxhighlight> A set of [https://github.com/mlund/mos-hardware/tree/main/examples code examples are available] (plasma effect, raster interrupt)
d1bbc9f78d593c1ab10776fbdb3fc8c9eb020fb4
348
347
2022-07-19T11:44:52Z
Wombat
9
Update docker info
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc:<syntaxhighlight lang="rust">
use mos_hardware::{c64, vic2}
(*c64::VIC).border_color.write(vic2::LIGHT_RED);
</syntaxhighlight> A set of [https://github.com/mlund/mos-hardware/tree/main/examples code examples are available].
ede2e9119e12cd41c9a57dcf976aa5b2f041c23f
351
348
2022-08-03T06:57:37Z
Wombat
9
Made the mos-hardware entry slightly less verbose
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]).
c6d0ced4632f538a2f132b20d546ede40b40db61
352
351
2022-08-09T11:39:42Z
Wombat
9
Added examples
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]).
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
209367b19d8fd1555a0ae7e9f56da500610b071f
C Inline Assembly
0
40
334
248
2022-07-03T03:33:52Z
Mysterymath
3
Add C and V flags to inline assembly.
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
[[Category:C]]
257dd32539a67ac8c81220a6675a40905c5e5ab2
339
334
2022-07-13T04:53:57Z
Mysterymath
3
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see the FAQ entry: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
[[Category:C]]
4f761f6a9b199228992bb85e3f5ddc34249a9643
340
339
2022-07-13T04:54:14Z
Mysterymath
3
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
[[Category:C]]
73a87c35e241a4f93d5a1d06a2f681a2151e8e1b
File:Nes-llvm-demo.png
6
51
335
2022-07-06T17:36:02Z
Jbyrd
1
wikitext
text/x-wiki
NES project in pure C99 using llvm-mos, by Steven Poulton
14e9406f1ebf49d7c618a05b7e970d34d1a55719
C interrupts
0
41
349
277
2022-07-26T21:06:25Z
34.214.158.144
0
/* Manual Interrupt Sequence */ fix minor grammar error
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is written entirely in assembly.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm">
cld
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda mos8(__rc6)
pha
lda mos8(__rc7)
pha
lda mos8(__rc8)
pha
lda mos8(__rc9)
pha
lda mos8(__rc10)
pha
lda mos8(__rc11)
pha
lda mos8(__rc12)
pha
lda mos8(__rc13)
pha
lda mos8(__rc14)
pha
lda mos8(__rc15)
pha
lda mos8(__rc16)
pha
lda mos8(__rc17)
pha
lda mos8(__rc18)
pha
lda mos8(__rc19)
pha
lda mos8(__rc20)
pha
lda mos8(__rc21)
pha
lda mos8(__rc22)
pha
lda mos8(__rc23)
pha
lda mos8(__rc24)
pha
lda mos8(__rc25)
pha
lda mos8(__rc26)
pha
lda mos8(__rc27)
pha
lda mos8(__rc28)
pha
lda mos8(__rc29)
pha
JSR body
pla
sta mos8(__rc29)
pla
sta mos8(__rc28)
pla
sta mos8(__rc27)
pla
sta mos8(__rc26)
pla
sta mos8(__rc25)
pla
sta mos8(__rc24)
pla
sta mos8(__rc23)
pla
sta mos8(__rc22)
pla
sta mos8(__rc21)
pla
sta mos8(__rc20)
pla
sta mos8(__rc19)
pla
sta mos8(__rc18)
pla
sta mos8(__rc17)
pla
sta mos8(__rc16)
pla
sta mos8(__rc15)
pla
sta mos8(__rc14)
pla
sta mos8(__rc13)
pla
sta mos8(__rc12)
pla
sta mos8(__rc11)
pla
sta mos8(__rc10)
pla
sta mos8(__rc9)
pla
sta mos8(__rc8)
pla
sta mos8(__rc7)
pla
sta mos8(__rc6)
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
4ce6783f606f8f6949f84be3b3a80996c7abee1a
File:Hello Commander X16.png
6
52
353
2022-08-12T06:10:16Z
Wombat
9
wikitext
text/x-wiki
Hello Commander X16 from C
49d8af32645c1f070e40c9ad41e380fce36c960e
NES overview
0
53
356
2022-08-25T06:35:30Z
Mysterymath
3
Start a NES page.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|0
|NROM
|<code>nes-nrom</code>
|-
|1
|MMC1
|<code>nes-mmc1</code>
|}
== Configuration ==
As far as is possible, mappers are configured by assigning symbol values.
42098ee16f576a2b1bc5a32ed0601fef259b055c
358
356
2022-08-25T16:15:20Z
Mysterymath
3
Soft delete NES overview.
wikitext
text/x-wiki
#REDIRECT [[NES targets]]
692bf0f02fa1b65da68cf37d07827941a940c977
NES targets
0
54
357
2022-08-25T16:11:58Z
2620:0:1000:5011:B4FD:4A41:D8E9:DAF5
0
Copy from "NES overview"
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|0
|NROM
|<code>nes-nrom</code>
|-
|1
|MMC1
|<code>nes-mmc1</code>
|}
== Configuration ==
As far as is possible, mappers are configured by assigning symbol values.
42098ee16f576a2b1bc5a32ed0601fef259b055c
359
357
2022-08-25T16:58:44Z
Mysterymath
3
Add iNES 2.0 symbols.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|0
|NROM
|<code>nes-nrom</code>
|-
|1
|MMC1
|<code>nes-mmc1</code>
|}
== Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files provided by the individual target. The semantics of this are given in each individual target section below.
45f4747fdc1bcc596b9c5f8ac7db4a2f10e370f0
360
359
2022-08-25T16:59:20Z
Mysterymath
3
Add leading zeros.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|}
== Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files provided by the individual target. The semantics of this are given in each individual target section below.
89021eb6fa7cd0c99a9e454c522cb475294a497c
361
360
2022-08-25T17:13:35Z
Mysterymath
3
Add mapper descriptions.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|}
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included by default.
|-
|<code>c-in-ram.ld</code>
|Place the C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included by default.
|}
== [https://www.nesdev.org/wiki/NROM NROM] ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the C sections into PRG-RAM bank 0.
|-
|<code>prg-ram-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-ram-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|}
17f13cb9baefa2ecd2b3f497466d95989957a99a
362
361
2022-08-25T17:28:34Z
Mysterymath
3
Add section descriptions.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included by default.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included by default.
|}
== [https://www.nesdev.org/wiki/NROM NROM] ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections into PRG-RAM bank 0.
|-
|<code>prg-ram-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-ram-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|}
5d37acf9a16cb5052d3ab889212ba751dd62577d
363
362
2022-08-25T17:55:43Z
Mysterymath
3
Clarify defaults.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM] ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections into PRG-RAM bank 0.
|-
|<code>prg-ram-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-ram-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
2e58bec8e001b192e2d18451da693740cd6c118e
368
363
2022-10-10T18:53:51Z
71.198.117.145
0
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections into PRG-RAM bank 0.
|-
|<code>prg-ram-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-ram-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
3cbe4af73ab89f59d489a7ba3f71b908721c457e
370
368
2022-11-04T20:42:23Z
Mysterymath
3
Add MMC3 description.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr-rom</code> section or any section that begins with <code>.chr-rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg-ram</code> section or any section that begins with <code>.prg-ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections into PRG-RAM bank 0.
|-
|<code>prg-ram-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-ram-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|-
|<code>prg-ram-banked-8.ld</code>
|One 8KiB bank is visible at 0x8000, and the last 3 banks are fixed contiguously at 0xa000.
|-
|<code>prg-ram-banked-mode-0.ld</code>
|Two independent 8KiB banks at 0x8000 and 0xa000, and the last 2 banks fixed contiguously at 0xc000.
|-
|<code>prg-ram-banked-mode-1.ld</code>
|Two independent 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-ram-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
67beeab09f3fdb937375b134f5249276aabed119
387
370
2022-12-23T15:03:16Z
Mysterymath
3
Fix section naming.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections into PRG-RAM bank 0.
|-
|<code>prg-ram-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-ram-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-ram-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-ram-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|-
|<code>prg-ram-banked-8.ld</code>
|One 8KiB bank is visible at 0x8000, and the last 3 banks are fixed contiguously at 0xa000.
|-
|<code>prg-ram-banked-mode-0.ld</code>
|Two independent 8KiB banks at 0x8000 and 0xa000, and the last 2 banks fixed contiguously at 0xc000.
|-
|<code>prg-ram-banked-mode-1.ld</code>
|Two independent 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-ram-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
c1be224039cb70b61f4cc26c853a2d12a2685a53
Linker Script
0
45
364
312
2022-08-25T20:27:02Z
Mysterymath
3
Describe OVERLAY extension.
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts.
See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior.
This page describes LLVM-MOS's extensions of LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>
* A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code>
Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size.
Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros.
<code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region.
The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
=== OVERLAY ===
As an extension, LLVM-MOS allows OVERLAY to be added to regular output section definitions:<syntaxhighlight>
.foo : OVERLAY { ... }
</syntaxhighlight>
[[Category:Linking]]
This prevents the linker from checking for overlapping VMAs for any sections marked with OVERLAY. This is more flexible than the existing OVERLAY syntax, which places additional constraints on the layout of the sections that are to receive overlapping VMAs.
af308ccee8c4518ed491c8588cdada5a4038bb15
401
364
2023-03-06T19:56:04Z
24.130.71.125
0
Remove OVERLAY extension; no longer needed.
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
See [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual] for a general reference to ld-style linker scripts.
See LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy] for LLD's extensions and interpretations of ld's behavior.
This page describes LLVM-MOS's extensions of LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>
* A memory region command: <code>FULL(''mem-region'')</code>, <code>TRIM(''mem-region'')</code>
Byte commands have the same syntax and semantics as those occurring in output section decriptions; they output the values of the given expressions as little-endian bytes of the corresponding size.
Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros.
<code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region.
[[Category:Linking]]
The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
63e97fbd508d33b8ef3fabb51e8410406486bfcb
Clangd
0
55
365
2022-09-24T21:24:29Z
Mysterymath
3
Added documentation for setting up clangd.
wikitext
text/x-wiki
The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK.
The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though:
# Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory.
# Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd.
# Set your editor so that the argument <code>--query-compiler="/path/to/llvm-mos-sdk/bin/*clang*"</code> is passed to clangd. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers.
== CMake + Visual Studio Code ==
To set up clangd when building using CMake and editing with Visual Studio Code:
# Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd.
# Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators.
# Build the project.
# In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd."
# In the setting titled "Clangd: Arguments", add the argument <code>--query-compiler="/path/to/llvm-mos-sdk/bin/*clang*"</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>.
# Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path.
# Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install.
df3ac35f07fad3595223eb982036f34b3d21d3a6
366
365
2022-09-25T00:45:45Z
Atn34
10
Update clang + vscode instructions to match what worked for me
wikitext
text/x-wiki
The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK.
The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though:
# Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory.
# Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd.
# Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers.
== CMake + Visual Studio Code ==
To set up clangd when building using CMake and editing with Visual Studio Code:
# Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd.
# Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators.
# Build the project.
# In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd."
# In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>.
# Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path.
# Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install.
d3ec6ddaf84fc432dfda41e0419d014e12d8ed04
C calling convention
0
17
367
326
2022-10-07T00:47:05Z
107.241.93.203
0
Remove WIPey text.
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.4669&rep=rep1&type=pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
3a214d1d4fab3fd42baee3553ef8bb6745dcd88e
384
367
2022-12-07T00:04:44Z
81.164.105.219
0
fix dead link
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
547d8327a1ed440e4079fcaa75962a78cba9ec77
Rust
0
50
369
352
2022-10-18T18:02:57Z
Wombat
9
Add link to github template for mos-hardware
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
4a1538395a7110b8429cafd74e5cb0d71ffd97dc
375
369
2022-11-13T19:40:26Z
Wombat
9
Add reference to mos-alloc crate
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] provides an allocate needed for heap allocation, ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
ef803e69ab6517bd5d33d039cef3655a908d4b02
376
375
2022-11-13T21:03:16Z
Wombat
9
Grammar
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
be7cbdbe2c01f0438076e2bf2b2823e9d994cfe6
385
376
2022-12-13T12:50:32Z
Wombat
9
/* Other resources */
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Code Studio.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware MOS Hardware crate] for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
1731264790299aedb9e4189c08267ff66f649fd2
386
385
2022-12-13T13:05:58Z
Wombat
9
Correct spelling
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
945cff244987a1f2653b1b4141e66ba953c977d1
388
386
2022-12-31T08:33:14Z
Wombat
9
Add more docker information
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker from Visual Studio Code ====
By providing a `.devcontainer.json` file, VSC can automatically launch a container for your project. See e.g. `[https://crates.io/crates/mos-hardware mos-hardware]` or other examples at the bottom of this page.
==== Using Docker from the terminal ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for aarch64] (faster on Apple silicon)
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
522dfa042d468a86855138a2bc6ff123115ce3cb
389
388
2022-12-31T08:35:45Z
Wombat
9
Docker information
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker from Visual Studio Code ====
By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See e.g. [https://crates.io/crates/mos-hardware mos-hardware] or other examples at the bottom of this page.
==== Using Docker from the terminal ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86 (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon)
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
699eba9f5205d847568493bda1f44c743fbfedf1
390
389
2022-12-31T08:51:31Z
Wombat
9
Add arm64 information to docker usage
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker from Visual Studio Code ====
By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See e.g. [https://crates.io/crates/mos-hardware mos-hardware] or other examples at the bottom of this page.
==== Using Docker from the terminal ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. You may replace <code>mrkits/rust-mos</code> with <code>mikaellund/rust-mos</code> for an arm-based build (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon)
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
296a81da53ab2667e06b2a28d5902f777a893e20
391
390
2022-12-31T08:57:48Z
Wombat
9
Docker documentation
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker from Visual Studio Code ====
By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See Hello World, mos-hardware, or other examples at the bottom of this page for how to set this up.
==== Using Docker from the terminal ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. You may replace <code>mrkits/rust-mos</code> with <code>mikaellund/rust-mos</code> for an arm-based build (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon)
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
48e24028ef37595c0abd4dbc6512b35b7c146e99
393
391
2023-01-04T21:51:51Z
Mrk
6
add mos-test
wikitext
text/x-wiki
The [https://github.com/mrk-its/rust-mos rust-mos project] is based on llvm-mos and llvm-mos-sdk and allows Rust programs to run on 6502. You may either build and install rust-mos from scratch by following the instructions [https://github.com/mrk-its/rust-mos here], or use a [https://hub.docker.com/r/mrkits/rust-mos pre-build Docker image].
=== Getting started ===
==== Using Docker from Visual Studio Code ====
By providing a <code>.devcontainer.json</code> file, VSC can automatically launch a container for your project. See Hello World, mos-hardware, or other examples at the bottom of this page for how to set this up.
==== Using Docker from the terminal ====
Install and run [https://www.docker.com/products/docker-desktop/ Docker desktop] and start the image from a terminal:<syntaxhighlight lang="bash">
docker pull mrkits/rust-mos
docker run -it --name myrustmos --entrypoint bash -v ${HOME}/Documents:/hostfiles mrkits/rust-mos
</syntaxhighlight>This will start up a new container where <code>$HOME/Documents</code> is mounted in <code>/hostfiles</code> on the virtual machine. This will work regardless of your local architecture, but will run slower on ''e.g.'' arm-based systems as the image is prepared for x86. You may replace <code>mrkits/rust-mos</code> with <code>mikaellund/rust-mos</code> for an arm-based build (see link below for building an image for arm64). If you have exited from the shell, you may return to the running container with:<syntaxhighlight lang="bash">
docker container exec -it myrustmos /bin/bash
</syntaxhighlight>A stopped container can be started with:<syntaxhighlight lang="bash">
docker container start -i myrustmos
</syntaxhighlight>
==== Minimal Example ====
For a minimal example, see [https://github.com/mrk-its/rust-mos-hello-world this github project]. Here you will also find instructions of how to use the above Docker container from Visual Studio Code.
=== Other resources ===
*[https://github.com/mrk-its/a800xl-utils Atari 800XL utility crate].
*[https://crates.io/crates/mos-alloc mos-alloc] crate for heap allocation needed by ''e.g.'' <code>alloc::vec</code> and <code>alloc::boxed</code>.
*[https://crates.io/crates/mos-test mos-test] crate providing alternative test harness that lets you write and run tests on all llvm-mos platforms
*[https://crates.io/crates/mos-hardware mos-hardware] crate for Commodore targets like C64 and MEGA65 that allows named access to registers, raster interrupts etc. ([https://github.com/mlund/mos-hardware/tree/main/examples examples]). There's also a [https://github.com/mlund/mos-hardware-template GitHub template] for starting a new project.
*How to build rust-mos [https://gist.github.com/mlund/ab2a99e441b7fdbb8c10c9b200f25680 Docker image for arm64] (faster on Apple silicon)
*Example: [https://github.com/mrk-its/rust-mos-hello-world Hello world].
*Example: [https://github.com/mrk-its/llvm-mos-ferris-demo Atari Ferris Demo]. See also [https://mrk.sed.pl/bevy-atari/#https://atari.ha.sed.pl/ferris.xex emulation] in browser.
*Example: [https://github.com/mrk-its/aoc2022 Advent of Code 2022 on Atari]. 🎄
0127aa1ba520d0d4e290cbb147dbfc36d9c47778
Optimization guide
0
56
371
2022-11-08T04:04:15Z
Mysterymath
3
Added an optimization Guide
wikitext
text/x-wiki
Clang is a modern, optimizing C compiler, and LLVM-MOS's fork of it is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and getting the most out of a modern compiler for embedded development requires some strategy. Some of the cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either.
The purpose of this guide is to help the reader gain a simple, but accurate, mental model of what Clang and LLVM-MOS are doing with their code behind the scenes, and how to help that engine to generate the best resulting assembly possible.
== Optimizing Compilers ==
Older optimizing compilers tended to more-or-less function as peephole optimizers: they naively emit a fixed set of instructions for each C construct, then they run optimization passes over the results later, looking for known inoptimal patterns and fixing them up.
You can get surprisingly far this way, but fairly quickly you run into a local optimum: there's no small, simple, local transformation that can improve the assembly, but the result is still much, much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better.
Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass makes some kind of decision or transformation of the code. Some passes are like the simple transformation passes, that replace one code snippet with another. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serializes as machine code and sent onward to the linker.
The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up.
== Rules ==
Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS:
* Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly).
* If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly calls and C to assembly declarations with <code>__attribute__((leaf))</code>. Otherwise, the compiler is forced to pessimize what those calls could call, which tends to also make it fall back to using a soft stack for the affected functions.
* Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). Keeping the values in local variables or expressions instead allow the compiler to worry about the value of the variable at various times in the program. Those values may be able to live in completely different locations. For example, a value may move into A to have some arithmetic done on it, and the result live in X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store.
* Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the compiler is required to make their address different from the address of every other pointer in the program. In practice, this means that the array will be copied. You can solve this by either making the array static, or making it global.
* Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you asked for.
* Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also somewhat reduce the compiler's ability to do so, so you may actually get slightly worse code for the extra effort, too.
* Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here.
* Prefer array indices of <256. For obvious reasons. LLVM-MOS comes equipped with the full gamut of addressing modes in it's instruction selector, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them.
[[Category:C]]
[[Category:Code generation]]
e5ef4f2e94585b35f4052aab2888d8aa6fa82089
372
371
2022-11-08T04:29:29Z
Mysterymath
3
/* Rules */
wikitext
text/x-wiki
Clang is a modern, optimizing C compiler, and LLVM-MOS's fork of it is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and getting the most out of a modern compiler for embedded development requires some strategy. Some of the cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either.
The purpose of this guide is to help the reader gain a simple, but accurate, mental model of what Clang and LLVM-MOS are doing with their code behind the scenes, and how to help that engine to generate the best resulting assembly possible.
== Optimizing Compilers ==
Older optimizing compilers tended to more-or-less function as peephole optimizers: they naively emit a fixed set of instructions for each C construct, then they run optimization passes over the results later, looking for known inoptimal patterns and fixing them up.
You can get surprisingly far this way, but fairly quickly you run into a local optimum: there's no small, simple, local transformation that can improve the assembly, but the result is still much, much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better.
Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass makes some kind of decision or transformation of the code. Some passes are like the simple transformation passes, that replace one code snippet with another. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serializes as machine code and sent onward to the linker.
The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up.
== Rules ==
Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS:
* Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly).
* If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly calls and C to assembly declarations with <code>__attribute__((leaf))</code>. Otherwise, the compiler is forced to pessimize what those calls could call, which tends to also make it fall back to using a soft stack for the affected functions.
* Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). Keeping the values in local variables or expressions instead allow the compiler to worry about the value of the variable at various times in the program. Those values may be able to live in completely different locations. For example, a value may move into A to have some arithmetic done on it, and the result live in X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store.
* Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global.
* Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you asked for.
* Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also somewhat reduce the compiler's ability to do so, so you may actually get slightly worse code for the extra effort, too.
* Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here.
* Prefer array indices of <256. For obvious reasons. LLVM-MOS comes equipped with the full gamut of addressing modes in it's instruction selector, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them.
[[Category:C]]
[[Category:Code generation]]
220dea85fefa79f064b4a1e1ce15b6ea9a2c105e
373
372
2022-11-09T05:31:03Z
Mysterymath
3
Copy edit the optimization guide.
wikitext
text/x-wiki
Clang is a modern, optimizing C compiler, and LLVM-MOS's fork is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and it takes some strategy to get the most out of a modern compiler on embedded platforms. Some of the standard cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either.
The purpose of this guide is to help the reader build a simple, but accurate, mental model of how Clang and LLVM-MOS work and how to help them generate better code.
== Optimizing Compilers ==
If they had optimization at all, older compilers tended to exclusively use "peephole optimization". They would emit a fixed assembly pattern for each C construct. Afterwards, they would run optimization passes over the results later, looking for known poor assembly patterns and replacing them with better ones.
You can get surprisingly far this way, but fairly quickly you run into a local optimum. Eventually, there won't be a small, simple, local transformation that can improve the assembly, but the result is still much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better.
Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass either analyzes the code or transforms it. Some passes are simple peephole passes. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serialized to machine code and sent onward to the linker.
The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up.
== Rules ==
Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS:
* Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly).
* If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly and assembly function declarations in C with <code>__attribute__((leaf))</code>. Otherwise, the compiler must pessimize what those calls could in turn call, which tends to also make it fall back to using a soft stack for the affected functions.
* Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). On the other hand, a local variable or expression may live in completely different locations as the program progresses. For example, a variable may move into A to have some arithmetic done on it, and the result move to X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store.
* Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global.
* Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you've asked for.
* Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also reduce the compiler's ability to do so, so you may get worse code for your effort, too.
* Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here.
* Prefer array indices of <256, for obvious reasons. LLVM-MOS can select the full gamut of addressing modes, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them.
[[Category:C]]
[[Category:Code generation]]
c9944ebb248d42f6609176a6029f10e5ad0f7746
374
373
2022-11-09T05:32:19Z
Mysterymath
3
Remove "for obvious reasons."
wikitext
text/x-wiki
Clang is a modern, optimizing C compiler, and LLVM-MOS's fork is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and it takes some strategy to get the most out of a modern compiler on embedded platforms. Some of the standard cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either.
The purpose of this guide is to help the reader build a simple, but accurate, mental model of how Clang and LLVM-MOS work and how to help them generate better code.
== Optimizing Compilers ==
If they had optimization at all, older compilers tended to exclusively use "peephole optimization". They would emit a fixed assembly pattern for each C construct. Afterwards, they would run optimization passes over the results later, looking for known poor assembly patterns and replacing them with better ones.
You can get surprisingly far this way, but fairly quickly you run into a local optimum. Eventually, there won't be a small, simple, local transformation that can improve the assembly, but the result is still much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better.
Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass either analyzes the code or transforms it. Some passes are simple peephole passes. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serialized to machine code and sent onward to the linker.
The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up.
== Rules ==
Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS:
* Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly).
* If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly and assembly function declarations in C with <code>__attribute__((leaf))</code>. Otherwise, the compiler must pessimize what those calls could in turn call, which tends to also make it fall back to using a soft stack for the affected functions.
* Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). On the other hand, a local variable or expression may live in completely different locations as the program progresses. For example, a variable may move into A to have some arithmetic done on it, and the result move to X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store.
* Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global.
* Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you've asked for.
* Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also reduce the compiler's ability to do so, so you may get worse code for your effort, too.
* Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here.
* Prefer array indices of <256. LLVM-MOS can select the full gamut of addressing modes, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them.
[[Category:C]]
[[Category:Code generation]]
4eb95605cc482e8d100d6ed26c99052cf3d13d7a
ELF specification
0
15
377
298
2022-11-22T19:53:00Z
209.79.16.69
0
Fixed the "no relocation type" entry
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.1.4 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (hexadecimal, 0x1966).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the following bits are set to one: EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_W65C02.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, including ASW, DEW, DEZ, INW, INZ, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|14
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[[Category:Linking]]
d5355a67441b5c723ff7db1cf918f442f16886c4
403
377
2023-06-12T18:40:17Z
Asie
12
Update to 0.2.1 in accordance with the discussions in PR #296.
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.2.1 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|14
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[[Category:Linking]]
a361e8915645fdfe04a9033e9efb009bbcc0726f
C Inline Assembly
0
40
378
340
2022-11-30T03:41:58Z
Mysterymath
3
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
[[Category:C]]
7c2585c8ba335f630466ee7bc965c46ae3273c8b
Modifiers
0
16
379
325
2022-11-30T07:09:57Z
Mysterymath
3
Add zeropage directive.
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte [[/cdn-cgi/l/email-protection|[email protected]]]</code> (or similar).
The directive <code>.zeropage <global> [, ...]</code> allows marking an external symbol as being in the zero page. Without this, each use would need to be annotated as <code>mos8</code>, or it would be conservatively relaxed to a 16-bit address.
[[Category:Assembly]]
5ba774dc9d8e3496470aa6c27e3fd17613f2a37e
380
379
2022-11-30T07:11:53Z
Mysterymath
3
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte [[/cdn-cgi/l/email-protection|[email protected]]]</code> (or similar).
[[Category:Assembly]]
ffe274b4a656bfb879c622ae292dc4af0a1081da
Assembler relaxation
0
19
381
330
2022-11-30T07:12:33Z
Mysterymath
3
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
# the symbol was marked with the <code>.zeropage <symbol></code> directive.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section .zp,"a",@nobits
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"az",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
3f8794205b585b0d3789e94cf982b6c70a08f799
382
381
2022-11-30T07:13:40Z
Mysterymath
3
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
# the symbol was marked with the <code>.zeropage <symbol></code> directive.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section .zp,"a",@nobits
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"az",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
Finally, if the symbol is undefined, the directive <code>.zeropage <symbol></code> can mark it as being in the zero page. It is an error to use this on a symbol defined in the current file; the determination for such symbols is made via the above mechanisms.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
42777a2f67df49775694bcd88eea461bae149bf4
383
382
2022-11-30T07:20:07Z
Mysterymath
3
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag.
# the symbol was marked with the <code>.zeropage <symbol></code> directive.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section .zp,"a",@nobits
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"az",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
Finally, the directive <code>.zeropage <symbol></code> can mark it as being in the zero page. This is most useful for undefined references, since it does nothing to actually place the symbol in the zero page; it just makes references to the symbol use 8-bit addressing modes.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
55f0eba61913f8b45f37e9252e03a54caacd3838
398
383
2023-02-17T23:39:06Z
209.79.16.69
0
Added an or.
wikitext
text/x-wiki
The 65xx series of processors has multiple types of instructions that can be encoded differently, depending on the size of the target address. For example, consider:
lda hello,x
In the 6502 case, this instruction could be encoded as <code>0xb5</code>, indicating that <code>hello</code> is an 8-bit (zero page) address. Alternately, it might be encoded as <code>0xbd</code>, indicating that hello is a 16-bit address.
In order to determine which encoding to use, the assembler must either (1) calculate the final value of the <code>hello</code> symbol, or (2) receive a hint as to which address space that <code>hello</code> should exist in.
LLVM's assembler has a feature called [https://eli.thegreenplace.net/2013/01/03/assembler-relaxation relaxation], in which individual instructions may be replaced with larger instructions, depending on how they would need to be encoded. However, the exact encoding of an instruction depends on the value that the symbol resolves to. And the value of that symbol might not be resolved until link time. By the time that the linker is running, the relaxation step of the assembler is well over.
This chicken-and-egg problem has multiple solutions. It might be possible to create different pseudo opcodes, such as lda8 and lda16, that map directly to one specific encoding. But this solution is incompatible with all existing 6502 code. It also might be possible to modify the LLVM linker to rerun the assembly relaxation step once all memory addresses are finalized during linking. The gcc toolchain has some support for this. But as of this writing, [https://reviews.llvm.org/D77694 this idea is still novel] for the LLVM toolchain.
The solutions we've gone with, provide multiple ways to tell the assembler and the linker that you want to put a symbol in zero page.
A symbol will resolve to an 8-bit address, and instruction encoding will occur under that assumption, if:
# the value of the symbol resolves, at assembly time, to an 8-bit non-zero constant; or
# the symbol is previously defined in a section with one of the following names: .zp, .zeropage, and .directpage; or
# the symbol is defined in a section marked with the special <code>z</code> flag, or
# the symbol was marked with the <code>.zeropage <symbol></code> directive.
If none of these conditions apply, then the symbol will refer to a 16-bit address, and instruction encoding will take place under that assumption.
So, one way to force a zero page access is fairly straightforward:
low_addr = 55 + 2 * 4
lda low_addr,x
Just define the address as a constant expression, and the assembler will deduce that a zero-page opcode is required. This is the classic solution, and most 8-bit programmers will be comfortable with it. The downside to this method, is that you have to do all memory management yourself, when ELF and the linker already have the information they need to assign those 8-bit addresses for you.
Another way to force a zero page access, is to tell the assembler your intention, by placing the symbol in one of the specially named zero-page sections:
.section .zp,"a",@nobits
low_short:
.byte 0x00 0x00
.section text
high_short:
.byte 0x01 0x00
lda low_short,x
A third way to force a zero page access is to mark the section with the special `z` flag:
.section .lowmemory,"az",@nobits
adrlowmemory: .ds.b 1
Here, the assembler will understand that the <code>adrlowmemory</code> symbol will eventually be located in zero page. Therefore all subsequent references to it will require one byte.
This solution lets the linker figure out the exact location in zero-page memory where the <code>low_short</code> symbol can go. It's up to the linker script to choose a reasonable zero-page location, on a per-target basis, for symbols marked as described above. Meanwhile, the assembler gets the hint it needs to make the <code>lda</code> instruction reference zero page, not 16-bit memory.
Finally, the directive <code>.zeropage <symbol></code> can mark it as being in the zero page. This is most useful for undefined references, since it does nothing to actually place the symbol in the zero page; it just makes references to the symbol use 8-bit addressing modes.
If you know nothing about this feature, then by default all symbols end up in 16-bit memory. This is the safest, but most memory hungry, option. This should work fine for most applications. But for those developers that actively want to put stuff in zero page, a Google search should lead them to this page and this solution.
[[Category:Assembly]]
7f817bbe7c2530e30b9582af82b747e5ba547fab
Findings
0
43
392
293
2023-01-03T19:03:31Z
2A01:586:A8F5:1:AB2:C42E:9A1B:6C19
0
wikitext
text/x-wiki
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
=== Stacks ===
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behave in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
=== Registers ===
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== ELF ==
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can performed detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
== GAS Assembler ==
The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments.
== Non-C languages ==
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
[[Category:Main]]
066168e60e712ab2a83596fbc1280e8f4f5f40a1
394
392
2023-01-07T12:51:52Z
180.150.37.168
0
/* ELF */ Fix a typo.
wikitext
text/x-wiki
== Advantages ==
With LLVM being as broad as it is, we gain a considerable number of advantages by being a first-class citizen of LLVM, beyond its code generator and optimizer.
Efficiently compiling C to the 6502 is a notoriously difficult problem. LLVM is an amazing platform for building compilers, and we've found that it presents some exciting new solutions, as well as new challenges.
There are two main obstacles when turning C into efficient machine code: stacks and registers.
=== Stacks ===
Past a certain point, processors were designed with C and other stack-bearing high-level languages in mind, while the 6502 decidedly predates this. C's runtime model explicitly states that the automatic variables of a function invocation must be kept separately on a per-invocation basis. The only efficient way to do this is with a stack. But the fastest available implementation of a sufficiently-large stack on the 6502 is quite slow, and much slower than the usual zeitgeist of assembly-language programming.
While it's true that the standard C runtime model is quite hostile to 6502 performance, the C standard provides broad latitude for alternative models that behave in all points "as if" it were the C model. In the broad space of possible alternatives, we've found a collection of techniques that broadly preserve C standard compatibility while emitting very high quality code. To put it another way, we go to great lengths to emit code that operates ''as if there were stacks'', without using stacks at all. LLVM's sophistication facilitates this. The analyses required are quite intricate, given that the compiler needs to correctly deal with recursive code as well as interrupt-driven reentrant code, but most of them are slight modifications to data structures already available in LLVM.
=== Registers ===
The 6502 has only three relatively general-purpose registers. One is an accumulator, while the other two are index registers. Most instructions bake in which register they operate on, which is quite different than most modern CPUs, which take register numbers in an operand field in the instruction encoding. Since registers are few, it's difficult to keep values in them for any length of time. Additionally, different instructions are constrained to use different registers. Few registers and tight register constraints are both poison to a traditional Chaitin-style register allocator, which causes a proliferation of code to spill values to and from the stack. This in turn requires additional registers, producing a horrible soup of spill-reload-spill-reload.
The original 6502 designers were well aware of the 6502's register limitations, so they provided zero-page addressing modes to compensate. The zero page locations are often treated much like processor registers; we take this view at face value (although we're not the first 6502 compiler to do so). We present ranges of zero page memory to LLVM in the form of ''imaginary registers''.
Instead of keeping instructions like LDA, LDX, and LDY separate, we merge them together into a logical instruction set. This would have one instruction: LD, which takes either A, X, or Y as argument. The distinction is subtle, but this makes the instruction set much more regular, which makes the register allocation problem easier to solve.
Together, these approaches take our backend from "alien nightmare" to "ugly duckling", not unlike x86 or AVR. Normal register allocation techniques apply, since the logical instruction set often treats different zero page locations and processor registers identically. While the instruction set remains a bit unusual, it's not *that* much worse than x86, and LLVM's register allocator is fully capable of handling it, even if the relationship is a bit strained.
== ELF ==
Instead of creating a custom object file format for llvm-mos, we've created an ELF target. While it's not likely that ELF is a good candidate for runtime loading and relocation on the 6502, support for it in POSIX operating systems is excellent. Existing tools like nm and ar can operate on llvm-mos binaries on an abstract level, and the LLVM versions of these tools bundled with the compiler can perform detailed reasoning about the contents. Using ELF gives us access to a whole scope of linker features; weak symbols, garbage collection, and most notably, link-time optimization.
== GAS Assembler ==
The llvm-mos project comes with a powerful GNU-compatible assembler for the 6502, in the form of [http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc]. All GNU assembler features can now be brought to bear on the 6502: weak symbols, section manipulation primitives, macros, etc. Some of this support extends into the inline assembly fragments understood by Clang, since it uses the underlying assembler to interpret these fragments.
== Non-C languages ==
LLVM IR is used as the primary backend for a number of languages, not just C. Accordingly, the compiler already has a degree of support for C++ and Rust. Accordingly, an opportunity exists to provide 6502 support for languages operating at a considerably higher level of abstraction than C, while still compiling down to relatively efficient machine code.
== Challenges ==
LLVM's complexity is both a blessing and a curse. While the sheer scope of the platform has given us the basis to solve the above problems, working with it is a daunting and complicated task. Much of the really important stuff is barely documented, and much of the compiler's passes are on the level of someone's PhD thesis.
Luckily, all that complexity gives us the support necessary to shoehorn the 6502 into it. It already supports AVR, weird DSP chips, GPUs, and WebAssembly (which doesn't even HAVE registers; it's a stack machine!). It even supports IBM SystemZ! All of the hooks necessary to emit good code for those platforms also help us get good code out of the 6502. We stand on the shoulders of weird, misshapen giants.
[[Category:Main]]
9aa0a2cda59385c02bc14d4a0235589bf632965f
Getting started
0
23
395
305
2023-01-07T13:11:41Z
180.150.37.168
0
Fixed a spelling error.
wikitext
text/x-wiki
See [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting Started with the SDK].
[[Category:Main]]
6a3d62fb01ca7687d857a3420666b591a6291d6b
Welcome
0
1
396
355
2023-01-23T23:03:58Z
Jbyrd
1
Changed to Discord invite
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Slack. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
b7e1e5b4ff309d97eac47a82d55e27931468fbad
397
396
2023-02-02T20:00:32Z
Jbyrd
1
Discord, not Slack.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the NES.
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
79b55cfc19fbf3d8bc9229f438ff153ec5dd7e83
400
397
2023-02-21T01:04:52Z
Jbyrd
1
Linked NES
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
c9ce97dc6c9b9437472d15a21d202100226a10b1
File:LLVM-MOS logo.png
6
57
399
2023-02-21T01:02:32Z
Jbyrd
1
wikitext
text/x-wiki
8-bit LLVM dragon logo
856d56c8ecb58ddafb7e2d5d794efe1bf12ef1d8
Code generation overview
0
22
402
328
2023-05-08T20:41:25Z
89.134.5.94
0
fix typo
wikitext
text/x-wiki
See also [https://www.youtube.com/watch?v=2lW3WHPtmKo this talk at 2022 EuroLLVM], which gives a high-level overview of llvm-mos's code generation strategy.
Like all LLVM backends, the bulk of the implementation exists in its own directory within the LLVM hierarchy, in llvm/lib/Target/MOS . llvm-mos uses LLVM's new GlobalISel architecture for instruction selection.
The instruction set of the 6502 isn't much more irregular than most modern CPUs, but the way that that regularity manifests itself is highly irregular. Modern CPUs generally have multiple operand slots for each opcode, so that addresses, indices, offsets, source registers, and destination registers can be directly specified. For the 6502, many of these values are indirectly encoded in the opcode itself. For example, the 6502 can load an absolute memory location to any register (A, X, or Y), but there are three opcodes to do so: LDA ADDR, LDX ADDR, and LDY ADDR. A more modern CPU would typically use one opcode: "LD R, ADDR".
To make the 6502 instruction set look more like what LLVM expects, we recast it "as if" it were a more modern instruction set. Thus, we report that the 6502 does have one "LD R, ADDR" instruction, where R is A, X, or Y. After code is generated in terms of these "logical instructions", we lower them down to the real 6502 instructions for final assembly output, machine code generation, and linking.
Because we model the 6502 instruction set in such a way as to be amenable to LLVM's algorithms, we benefit greatly from its machine independent optimization flows, from instruction selection, to register allocation, to basic block layout. There are some 6502-specific difficulties, but LLVM does provide relatively good means for targets (like ours) to sort them out ourselves, by providing pseudoinstructions and subroutine implementations that abstract away the complexity so LLVM doesn't need to know about it.
The original architects of the NMOS 6502 compensated for the 6502's small number of registers by providing 256 bytes of memory called Zero page, which could be accessed relatively quickly and cheaply by the processor. The llvm-mos C compiler utilizes a user-selectable range of zero-page memory, and performs nearly all of its operations there directly. We refer to this treatment of selectable ranges of zero page as imaginary registers, to distinguish them from LLVM's virtual registers. Code generation allocates and chooses imaginary registers for all operations that do not access 16-bit memory. Eventually, references to the imaginary registers are emitted as abstract symbols like "__rc12". The linker script will later map these to available locations in the zero page, depending on the target's specific memory map. Accordingly, the compiler's use of the zero page is highly customizable, and it can make use of highly discontiguous zero page fragments typical on real 6502 hardware.
Because we reserve a chunk of zero page memory for imaginary registers, and because LLVM has a great deal of specialized knowledge about pointer offsets and the like, llvm-mos can intelligently use a lot of the 6502's specialized addressing modes. For example, when a memory address that is a offset from a specific 16-bit pointer must be calculated, and that offset is 255 bytes or less, then llvm-mos can use LDA/STA (zp),y instructions to access that memory directly in a single instruction. This in turn means that llvm's GetElementPtr (GEP) instruction, can in many cases be reduced to a small handful of 6502 instructions. This is a big win when operating on pointed-to structs.
We can even try to produce 8-bit offsets where they wouldn't otherwise exist. For example, wherever possible, we rewrite 16-bit pointer loop indices to a sum of a 16-bit base and an 8-bit offset. Later on, the sum will be folded away into a LDA/STA (zp),y instruction. The advantage is that the loop increment is now just INY, not a full 16-bit increment. This optimization is possible because this pattern can be detected early on in the codegen pipelines, as LLVM allows us to do. See [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/lib/Target/MOS/MOSIndexIV.cpp MOSIndexIV.cpp] for more information on this particular optimization.
[[Category:Code generation]]
4ad7e05b585b428b3e9c65ea363bd11f2ec7fd89
Contributing
0
58
404
2023-06-13T00:57:06Z
Mysterymath
3
Added a contributing page.
wikitext
text/x-wiki
llvm-mos is a project with considerable scope, and considerably less resources. We can always use more help!
==SDK==
The [http://SDK https://github.com/llvm-mos/llvm-mos-sdk] contains the general C and C++ libraries and linker scripts that provide support for each platform. If you have knowledge of a 6502 system and are willing to monkey around with a GCC-alike compiler, then you could help by adding support for a new platform, extending the C library coverage for that platform's OS or hardware, or by adding new configurations of platforms (i.e., tape, boot disk, baremetal, SD card boot, etc.)
The [[Porting]] guide describes how to bring up a new target from scratch using the base compiler and <code>common</code> target, while the [[Extending SDK]] guide describes how to incorporate such changes into the SDK's CMake scripts. Reading these also provides a good baseline for understanding the layout of the SDK. Afterwards, just jump in and start picking apart how your (or the most similar to your) target works, and feel free to ask questions in the [https://discord.gg/D9gRm3aznh Discord].
==Compiler==
Contributing to the compiler is considerably less straightforward. (But it's still greatly appreciated!) LLVM is one of the world's premier industrial-duty compiler frameworks; it's tremendously powerful, but with great power (usually) comes great complexity, and LLVM is no exception. Learning how it works can be easier or harder, depending on what exactly it is you're trying to do, as you may have to only learn a tiny portion of its codebase, or you may need to make sweeping changes throughout.
A great resource for this is https://godbolt.org; this allows you to analyze any snippet of C or C++ in the llvm-mos compiler. Adding a "LLVM Opt Pipeline" view to the compiler in Godbolt shows the program as each pass gradually transform it from almost-C all the way to 6502 assembly. This is a great way to learn the breakdown of which responsibilities lie with which LLVM passes, and where one should go looking to make changes. After that, it's mostly a matter of reading code, reading comments, and playing with toy examples until you've built a sufficient mental model of how stuff works to make changes.
There are a few resources available to help kickstart the mental-model building process. First, there's an excellent [https://www.youtube.com/watch?v=objxlZg01D0 video overview] of how LLVM backends work; this is probably the best bang-for-your buck in learning LLVM's backend architecture. Second, there's a [https://www.youtube.com/watch?v=McByO0QgqCY video overview] and readable [https://llvm.org/docs/GlobalISel/index.html reference documentation] for how the Global Instruction Selector (GlobalISel) works; this is the process of lowering abstract instructions like "multiply" into moderately 6502-ey pseudo-instructions, and it's accordingly where most of the llvm-mos backend actually operates. There's also a variety of llvm-mos-specific passes; these implement logic that doesn't fit anywhere else in LLVM's existing framework.
Lastly, don't struggle alone (like I did); ask questions on our Discord instead! There's quite a bit of LLVM expertise floating around there at this point, and we might be able to save a lot of time on your end with just a little of ours.
4bffc9f65b07db6650297e97dded6179ed5e2434
405
404
2023-06-13T00:58:49Z
Jbyrd
1
Linked to Discord
wikitext
text/x-wiki
llvm-mos is a project with considerable scope, and considerably less resources. We can always use more help!
==SDK==
The [http://SDK https://github.com/llvm-mos/llvm-mos-sdk] contains the general C and C++ libraries and linker scripts that provide support for each platform. If you have knowledge of a 6502 system and are willing to monkey around with a GCC-alike compiler, then you could help by adding support for a new platform, extending the C library coverage for that platform's OS or hardware, or by adding new configurations of platforms (i.e., tape, boot disk, baremetal, SD card boot, etc.)
The [[Porting]] guide describes how to bring up a new target from scratch using the base compiler and <code>common</code> target, while the [[Extending SDK]] guide describes how to incorporate such changes into the SDK's CMake scripts. Reading these also provides a good baseline for understanding the layout of the SDK. Afterwards, just jump in and start picking apart how your (or the most similar to your) target works, and feel free to ask questions in the [https://discord.gg/D9gRm3aznh Discord].
==Compiler==
Contributing to the compiler is considerably less straightforward. (But it's still greatly appreciated!) LLVM is one of the world's premier industrial-duty compiler frameworks; it's tremendously powerful, but with great power (usually) comes great complexity, and LLVM is no exception. Learning how it works can be easier or harder, depending on what exactly it is you're trying to do, as you may have to only learn a tiny portion of its codebase, or you may need to make sweeping changes throughout.
A great resource for this is https://godbolt.org; this allows you to analyze any snippet of C or C++ in the llvm-mos compiler. Adding a "LLVM Opt Pipeline" view to the compiler in Godbolt shows the program as each pass gradually transform it from almost-C all the way to 6502 assembly. This is a great way to learn the breakdown of which responsibilities lie with which LLVM passes, and where one should go looking to make changes. After that, it's mostly a matter of reading code, reading comments, and playing with toy examples until you've built a sufficient mental model of how stuff works to make changes.
There are a few resources available to help kickstart the mental-model building process. First, there's an excellent [https://www.youtube.com/watch?v=objxlZg01D0 video overview] of how LLVM backends work; this is probably the best bang-for-your buck in learning LLVM's backend architecture. Second, there's a [https://www.youtube.com/watch?v=McByO0QgqCY video overview] and readable [https://llvm.org/docs/GlobalISel/index.html reference documentation] for how the Global Instruction Selector (GlobalISel) works; this is the process of lowering abstract instructions like "multiply" into moderately 6502-ey pseudo-instructions, and it's accordingly where most of the llvm-mos backend actually operates. There's also a variety of llvm-mos-specific passes; these implement logic that doesn't fit anywhere else in LLVM's existing framework.
Lastly, don't struggle alone (like I did); ask questions on our [https://discord.gg/D9gRm3aznh Discord] instead! There's quite a bit of LLVM expertise floating around there at this point, and we might be able to save a lot of time on your end with just a little of ours.
7878492954364fba8e70738dd2ce42614988e3e9
Optimization guide
0
56
406
374
2023-06-18T20:54:38Z
2600:387:6:807:0:0:0:2C
0
Add optimization flags
wikitext
text/x-wiki
Clang is a modern, optimizing C compiler, and LLVM-MOS's fork is no exception. Such a compiler is a powerful tool, but the 6502 is very much an embedded platform by modern standards, and it takes some strategy to get the most out of a modern compiler on embedded platforms. Some of the standard cc65 guidance can prevent getting the best results of LLVM-MOS, but writing totally naive cross-platform C doesn't necessarily give very good results either.
The purpose of this guide is to help the reader build a simple, but accurate, mental model of how Clang and LLVM-MOS work and how to help them generate better code.
== Optimizing Compilers ==
If they had optimization at all, older compilers tended to exclusively use "peephole optimization". They would emit a fixed assembly pattern for each C construct. Afterwards, they would run optimization passes over the results later, looking for known poor assembly patterns and replacing them with better ones.
You can get surprisingly far this way, but fairly quickly you run into a local optimum. Eventually, there won't be a small, simple, local transformation that can improve the assembly, but the result is still much worse than a human would write. You'd need advanced, global knowledge to produce better assembly, and you might have to make it worse before you make it better.
Modern compilers generally don't work like this; instead, they gradually massage the C a bit at a time. LLVM does this in a huge (100+) number of passes. Each pass either analyzes the code or transforms it. Some passes are simple peephole passes. Others gradually make bits of the program less abstract and more concrete. These may decide where to put values, how to lay out the stack, or how to copy values from one register to another. As the passes continue, the in-memory representation of the program gradually comes to resemble 6502 assembly more and more, until it maps to real assembly instructions one to one. At that point, the result is serialized to machine code and sent onward to the linker.
The key thing to take from this section is that LLVM essentially picks your program apart and reassembles it by the time it reaches assembly. In particular, things like variables and temporaries and expressions get almost entirely lost in the soup; eventually, all the compiler cares about is a graph of values, some depending on others for their computation, where they'll be put, what they need, and where they'll end up.
== Rules ==
Here's a succinct set of rules to follow to get good code with Clang/LLVM-MOS:
* Use <code>-Os</code> and <code>-flto</code>. These are the defaults for all targets, but they aren't automatically applied when compiling code snippets on the command line. LLVM-MOS seems to produce much better code overall when optimizing for size than for speed, and LTO enables a whole host of whole-program optimizations vital to getting good 6502 codegen.
* Use <code>-fnonreentrant</code> if you have function pointers. The compiler automatically tries to determine whether or not recursion is occurring, and it does an excellent job, except in the presence of function pointers. This is a limitation we'd like to remove, but in the meantime, you can use the <code>-fnonreentrant</code> compiler flag or the <code>__attribute__((nonreentrant))</code> function attribute to declare that a function is known never to have more than one instance active at a time. Without this, the compiler conservatively falls back to placing local variables on a soft stack, much like cc65 (although not nearly as poorly).
* If not using <code>-fnonreentrant</code>, make sure to annotate your inline assembly and assembly function declarations in C with <code>__attribute__((leaf))</code>. Otherwise, the compiler must pessimize what those calls could in turn call, which tends to also make it fall back to using a soft stack for the affected functions.
* Don't use global variables just for performance's sake. These are usually lowered to loads and stores to fixed locations (although sometimes the optimizer can see through them). On the other hand, a local variable or expression may live in completely different locations as the program progresses. For example, a variable may move into A to have some arithmetic done on it, and the result move to X for some indexing. Placing the result in a global could make it visible to another function though, so the compiler has a harder time avoiding the store.
* Don't use non-static function local constant arrays. Even if the values of these arrays are constant, the function might be called recursively, and the C standard requires that the arrays in different invocations of the function have different pointers. The compiler guarantees this by making a per-invocation copy of the array's initializer. While LLVM-MOS can currently elide the stack pointer use, it cannot yet elide the copy. It's easy to prevent though: just make the array static or global.
* Don't use the full results of wide integer calculations. It's often fine to compute values in wide types, so long as you only use part of the result. The compiler is usually pretty good at trimming down the computation. But, if you store a 64-bit sum somewhere in global memory, well, the compiler will probably give you what you've asked for.
* Don't reuse the same temporaries everywhere as an optimization. LLVM's register allocator is pretty darn good at assigning temporary values to memory locations and registers; there's usually no need to manually play register allocator. Pre-determining what values are going to share the same memory locations can also reduce the compiler's ability to do so, so you may get worse code for your effort, too.
* Use structs of arrays, not arrays of structs. We'd like to have a transformation from one to the other, but until we do, the usual 6502 C guidance applies here.
* Prefer array indices of <256. LLVM-MOS can select the full gamut of addressing modes, but it's not going to completely rearrange your data structures for you. It's up to you to size things such that it's possible to use them.
[[Category:C]]
[[Category:Code generation]]
f3b785cfcece15b2e246bc554e6dc42052ed2e4a
PCE target
0
59
407
2023-06-21T14:28:40Z
Asie
12
Created page with "VThe PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US. == Hardware description == * CPU: HuC6280 (Rockwell 65C02 derivative with addi..."
wikitext
text/x-wiki
VThe PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
|User
|Controlled by the software developer
|-
|3
|$6000 - $7FFF
|User
|
|-
|4
|$8000 - $9FFF
|User
|
|-
|5
|$A000 - $BFFF
|User
|
|-
|6
|$C000 - $DFFF
|User
|
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#* <code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code; it must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with nothing inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
88263a90583c3fbc58976ddb1335e2150ab2dc07
408
407
2023-06-21T14:29:37Z
Asie
12
add pull request information
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
|User
|Controlled by the software developer
|-
|3
|$6000 - $7FFF
|User
|
|-
|4
|$8000 - $9FFF
|User
|
|-
|5
|$A000 - $BFFF
|User
|
|-
|6
|$C000 - $DFFF
|User
|
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#* <code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code; it must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with nothing inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
c4075e522beb2e16dd9f6f5e43705571705dbee8
409
408
2023-06-21T14:31:48Z
Asie
12
minor visual improvements
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#* <code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code; it must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with nothing inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
a10c16d724d06f25e489c99ea2fd7ae58b4c4798
410
409
2023-06-21T14:45:21Z
Asie
12
minor naming scheme change, clarification
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can define <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by defining <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
8e80b02c3e1bec3c316da1bc0f137b199a94852a
411
410
2023-06-21T14:52:41Z
Asie
12
"defining" => "using macros"
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbank_N</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbank_N_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
865b40594a8b3d3b00d7017bd9616c2511a6e9cf
412
411
2023-06-21T15:45:55Z
Asie
12
document LMA->physical bank/address conversion, minor fixes
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
043406b3ba62cf45567359a78c69f88da5da5dff
415
412
2023-06-21T20:33:53Z
Asie
12
explain fixed bank storage in ROM
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
TODO: Document (should work roughly the same as on the NES targets).
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
b7e03ad0ade817826aa70ee932b6cf210f9c4348
416
415
2023-06-21T20:37:21Z
Asie
12
briefly document interrupts
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM; including SuperGrafx RAM) '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
cc2c39f1ffa00f7f5858b084b30d83c78b07e576
417
416
2023-06-21T20:39:51Z
Asie
12
add note on SuperGrafx support
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
7ce30d93feeddf59155dfbb842a0816c9f8fc08a
418
417
2023-06-21T20:41:53Z
Asie
12
explain .mlb files
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
e031380ac4f3a6f458c4cb21864070b0a73bacf5
419
418
2023-06-21T20:42:44Z
Asie
12
document bank index 0 unmapping in the context of interrupts
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
f1aba43ec9d2658147fd27504d3dac8c3d87bc24
420
419
2023-06-22T18:58:55Z
Asie
12
document pce_rom_vbankN_bank()
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>uint8_t pce_rom_vbankN_bank()</code> - returns the first physical bank ID ($00 - $7F) of VBank N;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage - matches <code>pce_rom_vbankN_bank()</code>'s output.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>PhysBank = (LMA - BankLMA) >> 13</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
1274294dcb4080c06c4ba35f57bcf50de9c2ae56
421
420
2023-06-22T21:05:06Z
Asie
12
fix PhysBank equation
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!Index
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Virtual banks ===
As the user gets to control up to 40KB out of the 64KB address space, a facility has been provided to make it easy to write C/C++ code utilizing groups of banks in arbitrary locations and sizes.
A ''virtual bank'' may consist of anywhere from one to five banks, mapped at any indexes between 2 and 6 inclusive. Its physical location in ROM is calculated at link time. To define one, do the following:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your virtual bank definitions, using macros of the format <code>PCE_ROM_VBANK_DEFINE(1, 2, 3)</code>.
#*<code>1</code> refers to the ID of the virtual bank; the linker script currently supports IDs from 1 to 127 inclusive. This ID will be used to refer to the virtual bank throughout code. It must be written in decimal. Virtual banks need not be defined sequentially; it is legal to define, for example, virtual banks <code>32</code> and <code>40</code> with none inbetween.
#* <code>2</code> refers to the starting location in 8KB units (the index), from 2 to 6 inclusive - see the above address space.
#* <code>3</code> refers to the size of the bank in 8KB units, from 1 to 5 inclusive; note that it must fit between indexes 2 and 6.
#* This example, therefore, creates a 24KB virtual bank with ID 1, mappable to the area between $4000 and $9FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#* <code>#define PCE_VBANK_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_VBANK_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_vbankN_set()</code> - maps VBank N to the CPU's address space;
* <code>uint8_t pce_rom_vbankN_bank()</code> - returns the first physical bank ID ($00 - $7F) of VBank N;
* <code>pce_rom_vbankN_call(void (*method)(void))</code> - safely maps VBank N and calls a function in it;
* the <code>__rom_vbankN</code> section, which the functions and variables stored inside VBank N should go to;
* the <code>__rom_vbankN_bank</code> symbol, which is relocated to the physical bank ID ($00 - $7F) at the linking stage - matches <code>pce_rom_vbankN_bank()</code>'s output.
Referring back to the above example - a 24KB virtual bank with ID 1 - this means that calling <code>pce_rom_vbank1_set();</code> will map the contents of virtual bank 1 to the area $4000-$9FFF.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in a VBank section is put in. Such code and data is accessible always.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that, unlike other virtual banks, fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
For virtual banks, the LMA's ''Bank'' value refers to the first bank of the virtual bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
6b84c14d800e213cc5a6129f69f89b38dd490c51
422
421
2023-06-25T12:52:35Z
Asie
12
replace virtual bank section with a description of what was actually merged
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
'''Note:''' This target is still a work in progress, and not yet upstreamed [https://github.com/llvm-mos/llvm-mos-sdk/pull/121].
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
b272a758bf57b260365c7015b5a5a41719fcb411
423
422
2023-06-25T12:52:50Z
Asie
12
remove non-upstreamed notice, as this target was upstreamed
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
* '''Q:''' Is the PC Engine CD supported?
* '''A:''' Not at this time. This will most likely be a derived target (pce-cd) in the future.
* '''Q:''' Is the SuperGrafx supported?
* '''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
e24fcf3f92944aaabdf976c401ea9ee35d5d044e
424
423
2023-06-30T16:44:58Z
Asie
12
initial pce-cd docs
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
d2663381c9db4031feb45bf217901f1db58618f5
425
424
2023-06-30T16:51:22Z
Asie
12
document pce-mkcd ipl.bin requirement
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in the root working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
b893c536db8866d7ef793a03ef056241ccd8f647
426
425
2023-06-30T16:51:40Z
Asie
12
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc.
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
dbb9a36e035a67334d9068f7ef5f6b3a9a6b3bb8
427
426
2023-06-30T18:26:03Z
Asie
12
document .cue file
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc.
To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below:<syntaxhighlight>
FILE output.iso BINARY
TRACK 01 MODE1/2048
INDEX 01 00:00:00
FILE output.wav WAV
TRACK 02 AUDIO
INDEX 01 00:00:00
</syntaxhighlight>
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
4a861e37041bb4d0c86adff7f3fd491f0e50e704
428
427
2023-06-30T18:26:56Z
Asie
12
formatting fix
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc.
To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below:
<code>
FILE output.iso BINARY
TRACK 01 MODE1/2048
INDEX 01 00:00:00
FILE output.wav WAV
TRACK 02 AUDIO
INDEX 01 00:00:00
</code>
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
175fff6882aa44a323166dafda42c6706e33cef0
429
428
2023-06-30T18:27:25Z
Asie
12
formatting fix 2
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc.
To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below:
FILE output.iso BINARY
TRACK 01 MODE1/2048
INDEX 01 00:00:00
FILE output.wav WAV
TRACK 02 AUDIO
INDEX 01 00:00:00
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
9fd3b97478b99cd741628e0a417f2d634ae8c3b9
430
429
2023-07-01T05:18:34Z
Asie
12
document pce-cd limitations
wikitext
text/x-wiki
The PCE platform corresponds to the NEC PC Engine, also known as the TurboGrafx-16 in the US.
== Hardware description ==
* CPU: HuC6280 (Rockwell 65C02 derivative with additional opcodes) @ 7.16MHz
** Redirected zero page at 0x2000, stack at 0x2100
** Interrupt control and timer
** Joypad port input/output
* Memory:
** 21-bit address space (8-bit bank index, 8 kilobytes per bank)
** 8 kilobytes of RAM (32 kilobytes on SuperGrafx), bank $F8 onwards
** Up to 1 megabyte of ROM, banks $00-$7F
** Memory mapped I/O, bank $FF
* VDC / VCE: Video Display Controller (handles drawing background and sprites) / Video Color Encoder (converts color indexes to RGB colors)
* PSG: Programmable Sound Generator
== Banking ==
The PC Engine's address space consists of eight consecutive 8KB banks, numbered from 0 to 7. llvm-mos follows target convention and maps them as follows:
{| class="wikitable"
|+PCE target - Address space
!MPR
!Addresses
!Bank
!Description
|-
|0
|$0000 - $1FFF
|$FF
|Memory mapped I/O
|-
|1
|$2000 - $3FFF
|$F8
|PC Engine RAM
|-
|2
|$4000 - $5FFF
| rowspan="5" |User
| rowspan="5" |Controlled by the software developer
|-
|3
|$6000 - $7FFF
|-
|4
|$8000 - $9FFF
|-
|5
|$A000 - $BFFF
|-
|6
|$C000 - $DFFF
|-
|7
|$E000 - $FFFF
|$00
|Fixed bank; contains IRQ/reset vectors:
* 0xFFF6: IRQ2 (external)
* 0xFFF8: IRQ1 (VDC)
* 0xFFFA: IRQ0 (timer)
* 0xFFFC: NMI (not used)
* 0xFFFE: RESET
|}
=== Bank mapping ===
Given that each 8KB bank can be mapped to five different locations by the user (and more if you're careful), the following method is provided to configure this mapping for LLVM-MOS emitted code:
# Create a header file to contain all of your bank definitions (for example, <code>bank.h</code>).
# Write your bank definitions, using macros of the format <code>PCE_ROM_BANK_AT(1, 3)</code>.
#*<code>1</code> refers to the ID of the bank; the linker script currently supports IDs from 1 to 127 inclusive. The ROM will automatically be padded with empty banks to fill in any gaps - this is useful if you want to use an external tool to inject data outside of LLVM-MOS later.
#* <code>3</code> refers to the MPR of the mapping, or the bank's location in the address space (see above) in 8KB units, from 2 to 6 inclusive.
#* This example, therefore, tells the compiler to map code in bank 1 between $4000 and $5FFF.
# Create an implementation file (for example, <code>bank.c</code>). Write the following lines:
#*<code>#define PCE_CONFIG_IMPLEMENTATION</code> - this must become ''before'' the include,
#* <code>#include <pce.h></code>
#* <code>#include "bank.h"</code>
# Include the header file (<code>bank.h</code>) ''without'' defining <code>PCE_CONFIG_IMPLEMENTATION</code> in any other file in which you wish to use virtual banks.
A defined virtual bank provides the following convenience methods:
* <code>pce_rom_bankN_map()</code> - maps bank N to the CPU's address space;
*<code>pce_rom_bankN_call(void (*method)(void))</code> - safely maps bank N and calls a function in it;
* the <code>__rom_bankN</code> section, which the functions and variables stored inside bank N should be placed in - for example, by using <code>__attribute__((section(__rom_bankN)))</code>.
=== Fixed area ===
Index 7 is fixed to bank $00, which is where all code and data not located in any bank is put in. Such code and data is always accessible.
Some code may, however, need a larger fixed area than eight kilobytes; for this, one can use the macro <code>PCE_ROM_FIXED_BANK_SIZE(n)</code> , where <code>n</code> is a value between 1 (8KB) and 6 (48KB). In this configuration, bank indexes starting from 7 and counting down will be dedicated to this fixed area; for example, for a size of <code>3</code> (24 KB), banks 5, 6, and 7 will be fixed.
Note that fixed areas larger than 8KB are non-contiguous in the ROM: the ''last'' bank of the fixed area is the first ROM bank, followed by the other banks. For example, for a size of <code>4</code> (32KB), bank <code>$00</code> will contain the last 8KB, <code>$01</code> the first 8KB, <code>$02</code> the second 8KB, and <code>$03</code> the third 8KB.
=== SuperGrafx RAM ===
The SuperGrafx provides 32 kilobytes of RAM rather than the console's default 8.
Currently, this may be taken advantage of by using the macro <code>PCE_SGX_RAM(n)</code>, where <code>n</code> is a value between 2 (16KB) and 4 (32KB). This will dedicate bank indexes starting from 2 and counting up for SuperGrafx memory, and allow using them in C code; for example, <code>PCE_SGX_RAM(4)</code> will allocate indexes 1 through 4 (addresses $2000 to $9FFF) to C code-visible RAM.
== Interrupts ==
Interrupts are provided in a manner similar to the NES target. The following entrypoints are defined:
* <code>irq_external</code> - IRQ2 (external interrupt),
* <code>irq_vdc</code> - IRQ1 (VDC),
* <code>irq_timer</code> - IRQ0 (timer),
* <code>nmi</code> - NMI (not emitted on PC Engine).
Interrupt handlers can be written in C by using <code>__attribute__((interrupt))</code> . This subject is explained in more detail on the [[C interrupts]] page.
== Linker details ==
=== ELF address space ===
{| class="wikitable"
|+PCE target - LMA format
!31-24
(Group)
!
!23-16
(Bank)
!
!15-0
(VMA)
|-
| rowspan="5" |0x00
| rowspan="5" |Base address space
|0x00 .. 0x7F
|HuCard ROM
| rowspan="9" |6502 address
|-
|0xF7
|Backup RAM (PC Engine CD)
|-
|0xF8
|RAM
|-
|0xF9 .. 0xFB
|RAM (SuperGrafx)
|-
|0xFF
|Memory mapped I/O
|-
| rowspan="3" |0x01
| rowspan="3" |Additional RAM
|0x40 .. 0x5F
|RAM ("Populous")
|-
|0x68 .. 0x7F
|RAM (Super System Card)
|-
|0x80 .. 0x87
|RAM (PC Engine CD)
|-
|0x02 .. 0x11
|"SF2" mapper
(Additional ROM banks)
|0x40 .. 0x7F
|HuCard ROM
|}
Note that, for a fixed bank larger than 8KB, the LMA's ''Bank'' value refers to its lowest bank. To recover the actual bank and in-bank address from the ELF data, one can use the following equation:
<code>FirstBank = ((LMA >> 16) & 0xFF)</code>
<code>PhysBank = FirstBank + ((LMA - BankLMA) >> 13)</code>
<code>PhysAddress = (LMA & 0x1FFF)</code>
where BankLMA refers to the nearest lower or equal, in terms of value, <code>__(.*vbank.+)_lma</code> symbol - the fixed bank is split into <code>vbank0a</code> and <code>vbank0b</code>.
== PC Engine CD ==
PC Engine CD support is provided via the <code>pce-cd</code> target. Developing for it has a few important differences from developing a cartridge-based project, outlined below.
=== Additional functionality ===
The PC Engine CD architecture provides an extensive BIOS, the functionality of which is documented in <code>cd/bios.h</code>.
The <code>pce-cd</code> target does not support resizing the fixed bank or mapping SuperGrafx RAM by default.
=== Banking ===
On the PC Engine CD, all data is stored in RAM; as such, instead of <code>__rom_bank</code> and <code>PCE_ROM_BANK_AT</code>, <code>__ram_bank</code> and <code>PCE_RAM_BANK_AT</code> is used itself. The range is also different:
* PC Engine CD, internal RAM: bank indexes 128 - 135
* Super System Card, additional RAM: bank indexes 104 - 127
The banks are populated from CD in their totality. To not duplicate RAM usage, the C <code>.data</code> section is stored alongside code in the first RAM bank, while <code>.bss</code> is stored in the console RAM by default.
The first RAM bank used is expected to be fixed at $4000.
=== Linking ===
There are four link scripts available, which can be specified by using f.e. <code>-Tbinary-cd.ld</code>:
* <code>binary-cd.ld</code> - "raw" PCE CD-ROM binary (64K RAM);
* <code>binary-scd.ld</code> - "raw" PCE Super CD-ROM binary (256K RAM);
* <code>ipl.ld</code> - Initial Program Loader (up to 40KB starting at bank 128), default;
* <code>ipl-ram.ld</code> - Initial Program Loader, stored in console RAM only (up to 1904 bytes, fits in one CD sector).
"Raw" binaries can be loaded by using the <code>pce_cdb_cd_exec</code> routine; "IPL" binaries can additionally be placed as the first file loaded on a CD image.
One can also link to contents of other files contained on a CD - see the below section for more information.
=== Building an ISO ===
To build an ISO, use the <code>pce-mkcd</code> tool bundled with llvm-mos-sdk. Provide it with an output filename (f.e. <code>output.iso</code>), followed by input files (either ELFs or any binary files). The first input file must be the IPL program.
The <code>pce-mkcd</code> tool resolves the additional symbols in specified ELF files:
* <code>__cd_[filename]_sector</code> - the 24-bit sector index of a given file,
* <code>__cd_[filename]_sector_count</code> - the size of a given file in sectors,
* <code>__cd_[filename]_bank_start</code> - the first bank of a given executable file,
* <code>__cd_[filename]_bank_end</code> - the last bank of a given executable file,
* <code>__cd_[filename]_bank_count</code> - the bank count of a given executable file,
* <code>__cd_[filename]_sym_[main]</code> - for executable files, this can be used to read the value of a symbol from another file.
In addition, one can pass <code>@list.txt</code> as any of the input arguments to <code>pce-mkcd</code> - this will read <code>list.txt</code>'s contents and use them as filenames.
Note that, as llvm-mos-sdk does not have permission to distribute this file, <code>pce-mkcd</code> requires a file called <code>ipl.bin</code> in its current working directory - it should be 2048 bytes in size and contain the contents of the first data sector of any PC Engine CD disc.
To add CD audio tracks or run the ISO in most PC Engine CD emulators, one should write a <code>.cue</code> file to accompany the ISO; an example file is provided below:
FILE output.iso BINARY
TRACK 01 MODE1/2048
INDEX 01 00:00:00
FILE output.wav WAV
TRACK 02 AUDIO
INDEX 01 00:00:00
== FAQ ==
* '''Q:''' Is the "SF2" mapper supported?
* '''A:''' Not at this time. While the LMA is prepared to support it, a dedicated linker configuration and VBank macros have to be written for this configuration.
*'''Q:''' Is the SuperGrafx supported?
*'''A:''' Yes! The VDC routines support using either VDC1 or VDC2, register definitions for the VPC are also available, and the SuperGrafx's extended RAM can be opted into (albeit - at this time - without banking support).
* '''Q:''' Can I safely remap non-User bank indexes?
* '''A:''' It depends:
** Bank index 0 (memory-mapped I/O) can be unmapped, provided that no user code - including library calls ''and interrupts'' - makes use of it.
** Bank index 2 (RAM) generally '''cannot''' be unmapped, as it contains the zero page's imaginary registers and the soft stack.
** Bank index 7 (fixed ROM) can probably be unmapped safely if IRQs are disabled, provided that no user code - including library calls - makes use of it.
* '''Q:''' What is the ".mlb" file generated alongside the ROM output?
* '''A:''' This generated file is a Mesen label file, containing symbol definitions compatible with Mesen 2's debugger.
74adfc8d66cf6cfe88addbbaaa2795e5e5629690
Linker Script
0
45
413
401
2023-06-21T18:21:29Z
Asie
12
document FULL/TRIM start, length; minor cleanup
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
For a general reference to ld-style linker scripts, see [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual].
For LLD's extensions and interpretations of ld's behavior, see LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy].
This page describes LLVM-MOS's extensions to LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>. Byte commands output the value of the given expression as little-endian bytes of the size corresponding to the command used. They have the same syntax and semantics as those which occur in output section descriptions.
* A memory region command: <code>FULL(''mem-region'', ''[start, [length]]'')</code>, <code>TRIM(''mem-region, [start, [length]]'')</code>. Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with an LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros. <code>FULL</code> causes the full output section to be emitted, from its origin through its full length. <code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region. The optional <code>start</code> argument allows specifying the starting position, in bytes, of the output section's data to be emitted. Note that this argument is relative to the beginning of the region, and not an absolute LMA. The optional <code>length</code> argument allows specifying the maximum number of bytes to be output as part of this memory region command. Note that if the section has fewer than <code>length</code> bytes remaining, the output won't be padded, but rather fewer bytes will be written. The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
[[Category:Linking]]
7383afa576b53da3222f49f79457e69d7193063f
414
413
2023-06-21T18:22:43Z
Asie
12
fix formatting
wikitext
text/x-wiki
The LLVM-MOS linker script format is an extension of the LLD linker script format, which is itself an extension of the GNU ld linker script format.
For a general reference to ld-style linker scripts, see [https://sourceware.org/binutils/docs/ld/Scripts.html the ld manual].
For LLD's extensions and interpretations of ld's behavior, see LLD's [https://lld.llvm.org/ELF/linker_script.html linker script implementations notes and policy].
This page describes LLVM-MOS's extensions to LLD's behavior.
===Custom Output Formats===
LLVM-MOS provides an extension to the <code>OUTPUT_FORMAT</code> syntax to allow specifying the precise bytes produced in the output file generated by the linker. This subsumes and extends the functionality provided by ld/LLD's <code>OUTPUT_FORMAT(binary)</code>.
The format of the extension is:
OUTPUT_FORMAT
{
''output-format-command''
''output-format-command''
...
}
The command form a script that outputs bytes to the output file, from top to bottom. When this section is present, the usual output file contains these bytes, but an additional file is created with the <code>.elf</code> file extension containing the ELF file.
Each ''output-format-comman''d can be:
* A byte command: <code>BYTE(''expr'')</code>, <code>SHORT(''expr'')</code>, <code>LONG(''expr'')</code>, <code>QUAD(''expr'')</code>.
Byte commands output the value of the given expression as little-endian bytes of the size corresponding to the command used. They have the same syntax and semantics as those which occur in output section descriptions.
* A memory region command: <code>FULL(''mem-region'', ''[start, [length]]'')</code>, <code>TRIM(''mem-region, [start, [length]]'')</code>.
Memory region commands output the loaded contents of the named memory region, as described in <code>MEMORY { }</code> . The contents of the region are the contents of all output sections with an LMA (load address) anywhere within the region. The relative locations of each byte are determined by their LMAs. Note that output sections doesn't need to be explictly assigned to the memory region with <code>></code> or <code>AT></code> to be included; it just needs to have at least one byte with an LMA that overlaps with the region. Any unreferenced sections of the memory region are filled with zeros.
<code>FULL</code> causes the full output section to be emitted, from its origin through its full length.
<code>TRIM</code> causes any trailing unreferenced bytes to be trimmed from the region before it's emitted. The last output byte corresponds to the highest LMA in any output section that overlaps with the region.
The optional <code>start</code> argument allows specifying the starting position, in bytes, of the output section's data to be emitted. Note that this argument is relative to the beginning of the region, and not an absolute LMA.
The optional <code>length</code> argument allows specifying the maximum number of bytes to be output as part of this memory region command. Note that if the section has fewer than <code>length</code> bytes remaining, the output won't be padded, but rather fewer bytes will be written.
The purpose of this extension is to allow defining custom file formats without adding code to the linker or <code>llvm-objcopy</code>. The byte commands can be used to construct header bytes based on the final locations of various symbols, and the load addresses can be chosen in such a fashion to provide a unique namespace for each byte that should be included in a binary image to be used by a 6502 target platform. See the SDK for examples of how this can be used.
[[Category:Linking]]
9d9ea0a461ccc1c8ab14d4325c97670d91bf4a52
ELF specification
0
15
431
403
2023-07-07T17:55:34Z
Asie
12
Add R_MOS_IMM16
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.2.2 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|14
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|15
|A 16-bit immediate value.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[[Category:Linking]]
f06df052fd9a75c95c8bb905db9b73e5ce1b1516
434
431
2023-08-10T20:48:40Z
Asie
12
0.2.3: add 45GS02
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.2.3 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_FK_DATA_4
|12
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|13
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|14
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|15
|A 16-bit immediate value.
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[[Category:Linking]]
668169396e63f7f05f21f9efc44304b5c788eeca
C calling convention
0
17
432
384
2023-07-26T21:45:02Z
2620:15C:B7:2:B139:5B1F:F7B6:D926
0
Clarify return value handling.
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
[[Category:C]]
e159a6799f3a56e48666d0f73e10a981cec044a5
454
432
2023-09-02T09:35:19Z
Wombat
9
Add table with examples illustrating calling conventions
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the (imaginary) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment
|-
|<code>char f(int a)</code>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|-
|<code>int * f(void *a)</code>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2</code>, <code>rc3</code>
|-
|<code>long f(long a, int b)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code>
|-
|<code>int f(int a, int b, void *c)</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|-
|<code>int f(void *a, char b, int c)</code>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|-
|<code>void f(void *a, uint32_t b)</code>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|-
|<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div div_t] a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4
|-
|<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div ldiv_t] a)</code>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4
|-
|<code>void f(uint64_t a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||By value (incl. <code>rc6-7</code>)
|}
140ebadc918ea13e08e36719fe8c63d77a6c9731
455
454
2023-09-02T09:47:53Z
Wombat
9
Add link to imaginary registers
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment
|-
|<code>char f(int a)</code>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|-
|<code>int * f(void *a)</code>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2</code>, <code>rc3</code>
|-
|<code>long f(long a, int b)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code>
|-
|<code>int f(int a, int b, void *c)</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|-
|<code>int f(void *a, char b, int c)</code>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|-
|<code>void f(void *a, uint32_t b)</code>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|-
|<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div div_t] a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4
|-
|<code>void f(struct [https://en.cppreference.com/w/c/numeric/math/div ldiv_t] a)</code>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4
|-
|<code>void f(uint64_t a)</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||By value (incl. <code>rc6-7</code>)
|}
fc422945f925752fcf73cbba187a04db5f3a7c71
Frequently asked questions
0
49
433
342
2023-08-07T06:06:42Z
Wombat
9
/* Rule 2: Inputs, outputs, and volatile. */
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234"::"R"(value));
}
</syntaxhighlight>The <code>R</code> constraint on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
[[Category:Main]]
30f443ab8126e917a4f3aba6f339cf74efd2a70f
Zero page
0
60
435
2023-08-21T16:53:24Z
Mysterymath
3
Add a page on zero page management
wikitext
text/x-wiki
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
13b8179881d2f30338ecf1bc2c3456629ae99665
436
435
2023-08-21T16:53:55Z
Mysterymath
3
Add categories
wikitext
text/x-wiki
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
[[Category:Code generation]]
[[Category:C]]
f27e78f9d8982ee44936a6c25c984baf34bfaa55
437
436
2023-08-22T21:32:21Z
Jbyrd
1
wikitext
text/x-wiki
In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[calling convention]], and let the compiler manage which variables are stored in zero page when.
However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes.
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
It is not recommended to "convince" the compiler to try to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job.
[[Category:Code generation]]
[[Category:C]]
654eeaed40ccc065bb80368be3db9a59e2c9d1b9
438
437
2023-08-22T21:34:46Z
Jbyrd
1
wikitext
text/x-wiki
In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]], and let the compiler manage which variables are stored in zero page when.
However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes.
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
It is not recommended to "convince" the compiler to try to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job.
[[Category:Code generation]]
[[Category:C]]
a62b8d72696f2521ed54f2ace42c3263992d52da
439
438
2023-08-22T21:37:44Z
Jbyrd
1
wikitext
text/x-wiki
In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]], and let the compiler manage which variables are stored in zero page when.
However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes.
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
It is not recommended to "convince" the compiler to try to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job.
[[Category:Code generation]]
[[Category:C]]
93b6dc32127e13b562ceff13751402a0ca9272e5
440
439
2023-08-22T21:39:16Z
Jbyrd
1
wikitext
text/x-wiki
In general, the default zero-page configurations for llvm-mos-sdk do not need to be modified. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]], and let the compiler manage which variables are stored in zero page when.
However, it is possible to convince the compiler to set aside zero page memory for custom assembly purposes.
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
It is not recommended to "convince" the compiler to store, for example, C global variables in zero page, to try to get the compiler to generate more efficient code. Let the register allocator do its job.
[[Category:Code generation]]
[[Category:C]]
0608591da0a03fb0fa13a5d4eb97cbf9f9224e02
441
440
2023-08-22T23:52:59Z
Mysterymath
3
Give more specific reasons why hand-annotation isn't usually necessary
wikitext
text/x-wiki
The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may require the compiler to set aside zero page memory. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]].
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program.
[[Category:Code generation]]
[[Category:C]]
fe4f09560cb24f6126bf26d0e6198ddd72438e91
442
441
2023-08-22T23:53:49Z
Mysterymath
3
Minor wording
wikitext
text/x-wiki
The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may reserving zero page memory from the compiler. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]].
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using <code>__attribute__((section(".zp''<type''>"))</code>. <code>''<type>''</code> is <code>.data</code> for C global variables with initializers, <code>.bss</code> for zero-initialized global variables, or the empty string for uninitialized global variables. The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler.
While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program.
[[Category:Code generation]]
[[Category:C]]
14548ad0ecd5e71dd8396c88b7bbc3ae79028635
File:C64-linux-boot-anim.gif.gif
6
61
443
2023-08-26T21:53:45Z
Jbyrd
1
wikitext
text/x-wiki
Onno Kortman's time-lapsed demo of booting Linux on a RISCV emulator on a C64 with REU
405e17ac35e2b0fe0512afc414a79ea2789679e3
Welcome
0
1
444
400
2023-08-26T21:54:41Z
Jbyrd
1
Added Linux gif demo
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
[[File:C64-linux-boot-anim.gif.gif|thumb|Onno Kortman's demo of Linux running in time lapse on a RISCV emulator on a C64 with REU]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
768210cebf665b30fed23c86744b43cf2ca93280
445
444
2023-08-26T21:55:23Z
Jbyrd
1
Onno Kortman's Linux gif does not animate on home page, so deleted.
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
There have been many failed attempts to create a 6502 backend for LLVM. Ours is the first to successfully compile working programs. The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Discord. If you're an experienced programmer, with a detailed understanding of the LLVM architecture, then [https://discord.gg/D9gRm3aznh please join our Discord group now] and help out.
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
c9ce97dc6c9b9437472d15a21d202100226a10b1
450
445
2023-09-01T16:40:33Z
Mysterymath
3
Update wording
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Discord. If you'd like to help out, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
212dd9fd3afa410e7c1427ccf1c72760a408a1c4
NES targets
0
54
446
387
2023-08-27T22:35:16Z
Cogwheel
13
/* INCLUDE Configuration */ corrected "ram" to "rom"
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections into PRG-RAM bank 0.
|-
|<code>prg-rom-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-rom-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections into PRG-RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One 8KiB bank is visible at 0x8000, and the last 3 banks are fixed contiguously at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two independent 8KiB banks at 0x8000 and 0xa000, and the last 2 banks fixed contiguously at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two independent 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
7a5be39720f43749dc7bdf4ee82c1c9643b33123
448
446
2023-08-30T04:58:46Z
Cogwheel
13
Clarified and added some examples for configuring the project
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|}
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-rom-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
b702ca369d694a6869cb85073927592904f72f86
449
448
2023-08-31T20:59:15Z
Cogwheel
13
Added a bunch more information on mappers with some example code
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Currently Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|}
For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell">
$ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes
</syntaxhighlight>
== Mapper Basics ==
The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos.
=== TLDR: What is a mapper? ===
Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup.
To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written".
=== PPU Banking ===
The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++">
bank_bg(0); // Use lower half of CHR-ROM for backgrounds
bank_spr(1); // Use upper half of CHR-ROM for sprites
</syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification.
=== CHR-ROM Banking ===
CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++">
// Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM
set_chr_bank_0(2);
set_chr_bank_1(3);
</syntaxhighlight>
=== PRG-RAM ===
The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM).
Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++">
set_wram_mode(WRAM_ON);
</syntaxhighlight>
===== PRG-RAM Banking =====
This gets a bit more complicated [ TODO ]
=== PRG-ROM Banking ===
This gets quite a bit more complicated [ TODO ]
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-rom-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
a49a2c86f08718721b45dfbb3bbecf6588215b2c
Character set
0
62
447
2023-08-29T01:13:07Z
Mysterymath
3
Add a page on character sets.
wikitext
text/x-wiki
Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code> )to target character sets.
With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced.
{| class="wikitable"
|+Supported Character Sets
!Target
!Name
!Syntax
!Notes
|-
|<code>c64</code>
|Unshifted PETSCII
|<code>U"HELLO"_u</code>
|C0 Control codes are passed through uninterpreted.
|-
|"
|Shifted PETSCII
|<code>U"hello"_s</code>
|"
|-
|"
|Unshifted Video
|<code>U"HELLO"_uv</code>
|C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|"
|Shifted Video
|<code>U"hello"_sv</code>
|"
|-
|"
|Unshifted Reverse Video
|<code>U"HELLO"_urv</code>
|C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these.
|-
|"
|Shifted Reverse Video
|<code>U"HELLO"_srv</code>
|"
|}
2b93767a870e7f67bc360dd48662b5dcb185525b
452
447
2023-09-02T00:09:59Z
Mysterymath
3
Add PET character set
wikitext
text/x-wiki
Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code> )to target character sets.
With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced.
{| class="wikitable"
|+Supported Character Sets
!Target
!Name
!Syntax
!Notes
|-
|<code>c64</code> , <code>pet</code>
|Unshifted PETSCII
|<code>U"HELLO"_u</code>
|C0 Control codes are passed through uninterpreted.
|-
|"
|Shifted PETSCII
|<code>U"hello"_s</code>
|"
|-
|"
|Unshifted Video
|<code>U"HELLO"_uv</code>
|C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|"
|Shifted Video
|<code>U"hello"_sv</code>
|"
|-
|"
|Unshifted Reverse Video
|<code>U"HELLO"_urv</code>
|C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these.
|-
|"
|Shifted Reverse Video
|<code>U"HELLO"_srv</code>
|"
|}
afddfadfab037ccbee8cbaa72037a1df914af9d9
453
452
2023-09-02T00:11:08Z
Mysterymath
3
Use descriptive target names
wikitext
text/x-wiki
Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code> )to target character sets.
With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced.
{| class="wikitable"
|+Supported Character Sets
!Target
!Name
!Syntax
!Notes
|-
|Commodore PET, Commodore 64
|Unshifted PETSCII
|<code>U"HELLO"_u</code>
|C0 Control codes are passed through uninterpreted.
|-
|"
|Shifted PETSCII
|<code>U"hello"_s</code>
|"
|-
|"
|Unshifted Video
|<code>U"HELLO"_uv</code>
|C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|"
|Shifted Video
|<code>U"hello"_sv</code>
|"
|-
|"
|Unshifted Reverse Video
|<code>U"HELLO"_urv</code>
|C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these.
|-
|"
|Shifted Reverse Video
|<code>U"HELLO"_srv</code>
|"
|}
beca2467bdcd84c7b12f0de9188fa056572d5fcb
Assembler
0
13
451
297
2023-09-01T22:26:42Z
Mysterymath
3
Add a link to the GNU assembler manual
wikitext
text/x-wiki
The MOS assembler understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
Traditionally, the [https://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html llvm-mc] tool is used as a standalone assembler within the LLVM universe, but our implementation permits MOS assembly to be implemented in multiple places, such as inline within a C program:
asm volatile ("JSR\t$FFD2" : "+a"(c));
To target MOS family processors, you will need to use a triple of "mos" (try: `-triple mos` or `-mcpu mos`) as a parameter to any tool.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, in the range of [-126,+129]. Since llvm-mc is [https://sourceware.org/binutils/docs/as/ GNU assembler] compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at compilation time.
The assembler understands that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
=== Target-specific directives ===
{| class="wikitable"
|+
!Syntax
!Description
|-
| .mos_addr_asciz <expression>, <num-digit-chars>
|Emits a fixed-length decimal ASCII string of a relocatable expression. Primary use case is for generating the BASIC header on C64.
|}
[[Category:Assembly]]
e713c649c0ef9337477e22830d8ba070e6fd69c9
C calling convention
0
17
456
455
2023-09-02T10:10:33Z
Wombat
9
Add C syntax
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2</code>, <code>rc3</code>
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code>
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|-
|<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">void f(uint64_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||By value (incl. <code>rc6-7</code>)
|}
26c49c5b110792622d9615aea8a5db52fddccb54
457
456
2023-09-02T11:52:23Z
Wombat
9
Add return struct examples
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output / Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2</code>, <code>rc3</code>
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2</code>, <code>rc3</code>
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|-
|<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||By value if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2, rc3</code> (value)
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|
|
|<code>a</code>
|<code>a</code>
|<code>rc2, rc3</code> (pointer; ''implicit'' 1st arg.)
|-
|<source lang="c" enclose="none">void f(uint64_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||Always by value (incl. <code>rc6-7</code>)
|}
f32929f194168f3b66dd6693ddf32b0ac38d2607
458
457
2023-09-02T12:04:17Z
Wombat
9
Update calling convention example table
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|
|-
|<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By value if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return by value
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|<code>rc2-3</code>
|Return via ''implicit'' 1st arg.
|-
|<source lang="c" enclose="none">void f(uint64_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||
|By value, incl. <code>rc6-7</code>
|}
19d32293ef42ac3ebdd3d9197bb05c1dc6fc71e8
459
458
2023-09-02T12:33:40Z
Wombat
9
Update comments in table
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args always...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (also <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Fill from left to right
|-
|<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By value if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return by value
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|<code>rc2-3</code>
|Return via ''implicit'' 1st arg.
|}
a413fcb027422afb418f80ba711983f3ec0f7069
460
459
2023-09-02T12:38:00Z
Wombat
9
Update table comments
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(void *a, uint32_t b)</source>||<code>b</code>||<code>b</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||
|
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By value if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return by value
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|<code>rc2-3</code>
|Return via ''implicit'' 1st arg.
|}
0f5fa8a5a32b88ea5c947086cf7e4253ec194169
461
460
2023-09-02T18:46:50Z
Wombat
9
Change "by value" to "by members"
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|<code>rc2-3</code>
|Return via ''implicit'' 1st arg.
|}
dcb2d6226f388ce54e03257f46590cb8cb7faa55
462
461
2023-09-02T19:28:23Z
Mysterymath
3
Clarify wording
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write to ''implicit'' 1st arg.
|}
f642ab45b25f5e14e68b2e6b5f31d056f88ce62a
463
462
2023-09-02T19:28:49Z
Mysterymath
3
wikitext
text/x-wiki
* A, X, Y, C, N, V, Z and RS1 to RS9 (RC2 to RC19) are caller-saved. A function may freely overwrite any of these, and the function's callers have to just deal with it.
* PC, S, D, I, RS0 (RC0 and RC1), and RS10 to RS15 (RC20 to RC31) are callee-saved. A function can use them freely, but before it returns it has to put them back exactly the way it found them, and the function's callers can rely on this behavior.
* Arguments are assigned from left to right. The return value is assigned in the same manner as the first argument.
* The bytes composing numeric arguments are passed individually in A, then X, then RC2 to RC15.
* Pointers are passed through RS1 through RS7.
* If no registers remain available, values are passed through the soft stack.
* Aggregate types (structs, arrays, etc.) 4 bytes or smaller are split into their individual value types, and each is passed individually. Such types are also returned by value.
* Aggregate types larger than 4 bytes are passed by pointer. The pointer is managed entirely by the caller, and may or may not be on the soft stack. The callee is free to write to the memory; the caller must consider the memory overwritten by the call. Such types are returned by a pointer passed as an implicit first argument. The resulting function then returns void.
* Variable arguments (those within the ellipses of the argument list) are passed through the stack. Named arguments before the variable arguments are passed as usual: first in registers, then stack. Note that the variable argument and regular calling convention differ; thus, variable argument functions must only be called if prototyped. The C standard requires this, but many platforms do not; their variable argument and regular calling conventions are identical. A notable exception is Apple ARM64.
For insight into the design of performant calling conventions, see the following work by Davidson and Whalley. By their convention, this plaftorm uses the "smarter hybrid" method, since LLVM performs both shrink wrapping and caller save-restore placement optimizations, while using both callee-saved and caller-saved registers when appropriate.
Our calling convention is roughly based on RISC-V, suggested after a discussion with one of their working group members.
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Methods for Saving and Restoring Register Values across Function Calls: Software--Practice and Experience Vol 21(2), 149-165 (February 1991)]
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write through ''implicit'' 1st arg pointer
|}
90bb88971c006ba641e971e4dad980b60f438438
File talk:LLVM-MOS logo.png
7
63
464
2023-09-04T10:49:30Z
37.113.180.113
0
Created page with "I love this madly, how can I use it? ~~~~"
wikitext
text/x-wiki
I love this madly, how can I use it? [[Special:Contributions/37.113.180.113|37.113.180.113]] 10:49, 4 September 2023 (UTC)
e4e8b0ba5c72517b2edcf37e718196d302819fab
NES targets
0
54
465
449
2023-09-05T01:39:53Z
Jroweboy
14
Added Action 53 mapper
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Currently Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|-
|028
|Action 53
|<code>nes-action53</code>
|}
For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell">
$ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes
</syntaxhighlight>
== Mapper Basics ==
The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos.
=== TLDR: What is a mapper? ===
Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup.
To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written".
=== PPU Banking ===
The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++">
bank_bg(0); // Use lower half of CHR-ROM for backgrounds
bank_spr(1); // Use upper half of CHR-ROM for sprites
</syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification.
=== CHR-ROM Banking ===
CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++">
// Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM
set_chr_bank_0(2);
set_chr_bank_1(3);
</syntaxhighlight>
=== PRG-RAM ===
The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM).
Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++">
set_wram_mode(WRAM_ON);
</syntaxhighlight>
===== PRG-RAM Banking =====
This gets a bit more complicated [ TODO ]
=== PRG-ROM Banking ===
This gets quite a bit more complicated [ TODO ]
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
== MMC1 ==
Different PRG-ROM sizes of the MMC1 require different numbers of reset stubs to be available, so the PRG-ROM size must be set by INCLUDE, not by symbol. The below INCLUDE files automatically set the <code>__prg_rom_size</code> header field to the corresponding value. The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since the C runtime model currently used by the compiler is only really practical if a fixed bank is present where e.g. libcalls can be placed.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-16.ld</code>
|16 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32.ld</code>
|32 KiB of PRG-ROM without banking.
|-
|<code>prg-rom-32-banked.ld</code>
|32 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3, bank 0.
|-
|<code>prg-rom-64.ld</code>
|64 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-128.ld</code>
|128 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3.
|-
|<code>prg-rom-256.ld</code>
|256 KiB of PRG-ROM with banking. Initializes to PRG-ROM bank mode 3. Included in default linker script.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== Action 53 ==
This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000.
eb93313d6f55976542aa12919decf1bb55b596b3
469
465
2023-09-14T18:17:29Z
Mysterymath
3
Update MMC1 to reflect simplication.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Currently Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|-
|028
|Action 53
|<code>nes-action53</code>
|}
For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell">
$ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes
</syntaxhighlight>
== Mapper Basics ==
The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos.
=== TLDR: What is a mapper? ===
Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup.
To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written".
=== PPU Banking ===
The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++">
bank_bg(0); // Use lower half of CHR-ROM for backgrounds
bank_spr(1); // Use upper half of CHR-ROM for sprites
</syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification.
=== CHR-ROM Banking ===
CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++">
// Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM
set_chr_bank_0(2);
set_chr_bank_1(3);
</syntaxhighlight>
=== PRG-RAM ===
The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM).
Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++">
set_wram_mode(WRAM_ON);
</syntaxhighlight>
===== PRG-RAM Banking =====
This gets a bit more complicated [ TODO ]
=== PRG-ROM Banking ===
This gets quite a bit more complicated [ TODO ]
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with an assembly file or inline assembly . For example:<syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
== MMC1 ==
The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked.ld</code>
|Up to 256KiB of banked PRG-ROM.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== Action 53 ==
This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000.
a9c8e79a307482468a380fad897d126315cfb3ce
471
469
2023-09-16T06:07:39Z
Mysterymath
3
Refer to ines.h macros
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Currently Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|-
|028
|Action 53
|<code>nes-action53</code>
|}
For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell">
$ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes
</syntaxhighlight>
== Mapper Basics ==
The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos.
=== TLDR: What is a mapper? ===
Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup.
To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written".
=== PPU Banking ===
The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++">
bank_bg(0); // Use lower half of CHR-ROM for backgrounds
bank_spr(1); // Use upper half of CHR-ROM for sprites
</syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification.
=== CHR-ROM Banking ===
CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++">
// Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM
set_chr_bank_0(2);
set_chr_bank_1(3);
</syntaxhighlight>
=== PRG-RAM ===
The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM).
Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++">
set_wram_mode(WRAM_ON);
</syntaxhighlight>
===== PRG-RAM Banking =====
This gets a bit more complicated [ TODO ]
=== PRG-ROM Banking ===
This gets quite a bit more complicated [ TODO ]
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with the macros in <code>[https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/nes/ines.h ines.h]</code>, inline assembly, or an assembly . For example:<syntaxhighlight lang="c">
// main.c
#include <ines.h>
MAPPER_CHR_ROM_KB(0);
MAPPER_CHR_RAM_KB(8);
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight><syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
== MMC1 ==
The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked.ld</code>
|Up to 256KiB of banked PRG-ROM.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== Action 53 ==
This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000.
8929ddd1a9d2b6e2022c688d2a2c30d86231ce6c
472
471
2023-09-16T06:08:03Z
Mysterymath
3
/* Symbol Configuration */
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Currently Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|-
|028
|Action 53
|<code>nes-action53</code>
|}
For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell">
$ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes
</syntaxhighlight>
== Mapper Basics ==
The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos.
=== TLDR: What is a mapper? ===
Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup.
To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written".
=== PPU Banking ===
The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++">
bank_bg(0); // Use lower half of CHR-ROM for backgrounds
bank_spr(1); // Use upper half of CHR-ROM for sprites
</syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification.
=== CHR-ROM Banking ===
CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++">
// Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM
set_chr_bank_0(2);
set_chr_bank_1(3);
</syntaxhighlight>
=== PRG-RAM ===
The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM).
Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++">
set_wram_mode(WRAM_ON);
</syntaxhighlight>
===== PRG-RAM Banking =====
This gets a bit more complicated [ TODO ]
=== PRG-ROM Banking ===
This gets quite a bit more complicated [ TODO ]
== Sections ==
=== PRG-ROM ===
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
=== CHR-ROM ===
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
=== PRG-RAM ===
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
== Symbol Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with the macros in <code>[https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/nes/ines.h ines.h]</code>, inline assembly, or an assembly . For example:<syntaxhighlight lang="c">
// main.c
#include <ines.h>
MAPPER_CHR_ROM_KB(0);
MAPPER_CHR_RAM_KB(8);
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight><syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low 2 bytes of byte 14
|-
|<code>__default_expansion_device</code>
|Low 6 bytes of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== INCLUDE Configuration ==
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
== [https://www.nesdev.org/wiki/NROM NROM], CNROM ==
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
== MMC1 ==
The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked.ld</code>
|Up to 256KiB of banked PRG-ROM.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== MMC3 ==
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
=== INCLUDE Configuration ===
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
== Action 53 ==
This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000.
92eee72902981237cbc83be88502b7933c8f3cee
474
472
2023-09-16T12:40:03Z
Asie
12
Add new mappers, clean up category tree, add ELF address space documentation, fix a few typos.
wikitext
text/x-wiki
The NES is a particularly challenging compiler target, due to the extremely large number of configurations supported by the various "mapper" ASICs on its cartridges.
The generic <code>nes</code> target contains only functionality that is generically applicable across mapper chips. Individual mappers each have their own derived target:
{| class="wikitable"
|+Currently Supported Mappers
!Number
!Name
!Target
|-
|000
|NROM
|<code>nes-nrom</code>
|-
|001
|MMC1
|<code>nes-mmc1</code>
|-
|002
|UNROM
|<code>nes-unrom</code>
|-
|003
|CNROM
|<code>nes-cnrom</code>
|-
|004
|MMC3
|<code>nes-mmc3</code>
|-
|028
|Action 53
|<code>nes-action53</code>
|-
|030
|UNROM-512
|<code>nes-unrom-512</code>
|}
For example, to build a C++ file in a project targeting MMC1, you would use a command like:<syntaxhighlight lang="shell">
$ mos-nes-mmc1-clang++ main.cpp -Os -o main.nes
</syntaxhighlight>
== Mapper Basics ==
The NESdev Wiki has a [https://www.nesdev.org/wiki/Mapper comprehensive guide on NES Mappers]. Here are the basics you'll need to get up and running with a mapper using llvm-mos.
=== TLDR: What is a mapper? ===
Mapper chips most commonly allow a cartridge have ''a lot'' more memory than the NES would otherwise support. They do this by letting you redirect a region of the NES' memory space (a ''bank'') to a different place in your cartridge's memory. Some offer advanced features like synchronizing code with a particular scan line of the screen drawing. This allows for special effects like having a HUD at the bottom of the screen or doing certain kinds of parallax effects. The most basic mappers simply allow you to include an extra RAM chip on the cartridge, with or without battery-backup.
To control the mapper, a game will usually "write" to specific addresses in the ROM. No data is written since the memory is read-only, but the mapper chip "sees" this attempt and responds to the data that is being "written".
=== PPU Banking ===
The NES allocates an 8KiB address space for CHR-ROM, from <code>0x0000</code> to <code>0x1FFF</code>. Conceptually these are split into two separate 4KiB regions which can be used for sprite data, background data, or both. So you might have the title screen using the lower 4KiB of ROM for both backgrounds and sprites, but then the main game switches to the upper bank for the sprites.<syntaxhighlight lang="c++">
bank_bg(0); // Use lower half of CHR-ROM for backgrounds
bank_spr(1); // Use upper half of CHR-ROM for sprites
</syntaxhighlight>'''Note:''' This form of banking is built into the NES and does not require any mapper. The rest of this article is referring to mapper-based banking when it uses "banking" without qualification.
=== CHR-ROM Banking ===
CHR-ROM banking allows you to select a different region of the physical memory in your cartridge to map to some region of the 8 KiB CHR-ROM. For example, with MMC1, you can have up to 128 KiB of CHR-ROM in your cartidge/image and select any 4 KiB bank using a 5-bit address (0-31).<syntaxhighlight lang="c++">
// Map PPU CHR-ROM to the 3rd and 4th 4KiB regions of physical ROM
set_chr_bank_0(2);
set_chr_bank_1(3);
</syntaxhighlight>
=== PRG-RAM ===
The addresses available for cartridges to use in the PRG-ROM space range from <code>0x4020</code> to <code>0xFFFF</code>. The simplest cartridges would connect a 32 KiB ROM chip and enable or disable it based on the highest bit of the memory address being read. Using the address range between <code>0x4020</code> and <code>0x7FFF</code> is a bit more complicated since it takes more bits of the address to distinguish it from other ranges. It is significantly easier to use the <code>0x6000</code>-<code>0x7FFF</code> range, so a lot of cartridges included an 8KiB RAM chip behind a simple logic gate to check the upper bits of the address. This area is often called Work RAM (WRAM).
Depending on the mapper you may need to call a function to enable WRAM. E.g. with MMC3 you would do:<syntaxhighlight lang="c++">
set_wram_mode(WRAM_ON);
</syntaxhighlight>
===== PRG-RAM Banking =====
This gets a bit more complicated [ TODO ]
=== PRG-ROM Banking ===
This gets quite a bit more complicated [ TODO ]
== Mapper/iNES Header Configuration ==
As far as is possible, mappers are configured by assigning symbol values. These control the contents of the [https://www.nesdev.org/wiki/NES_2.0 iNES 2.0 header] in the resulting binary, as well as the addresses assigned to program sections. Some of the following values have logical sizes less than the full 32-bits available for symbol values. In such cases, only the lowest order bits are considered. For example, <code>__prg_nvram_size_raw</code> corresponds to the high nibble of header field 10. This means that the lowest nibble of <code>__prg_nvram_size_raw</code> is mapped to the high nibble of header field 10.
You can set these symbols with the macros in <code>[https://github.com/llvm-mos/llvm-mos-sdk/blob/main/mos-platform/nes/ines.h ines.h]</code> or manually - using inline assembly or a separate assembly file. For example:<syntaxhighlight lang="c">
// main.c
#include <ines.h>
MAPPER_CHR_ROM_KB(0);
MAPPER_CHR_RAM_KB(8);
</syntaxhighlight><syntaxhighlight lang="c">
// main.c
asm(".globl __chr_rom_size\n"
"__chr_rom_size = 0\n"
".globl __chr_ram_size\n"
"__chr_ram_size = 8\n");
</syntaxhighlight><syntaxhighlight lang="asm">
; config.s
.global __prg_rom_size, __chr_rom_size, __prg_ram_size
.global __mirroring, __four_screen
; Kilobytes
__prg_rom_size = 512
__chr_rom_size = 256
__prg_ram_size = 8
; Flags
__mirroring = 1 ; horizontal mirroring
</syntaxhighlight>
{| class="wikitable"
|+Configuration Symbols
!Symbol
!Description
|-
|<code>__prg_rom_size</code>
|KiB of PRG-ROM
|-
|<code>__chr_rom_size</code>
|KiB of CHR-ROM
|-
|<code>__prg_ram_size</code>
|KiB of PRG-RAM
|-
|<code>__prg_nvram_size</code>
|KiB of PRG-NVRAM
|-
|<code>__chr_ram_size</code>
|KiB of CHR-RAM
|-
|<code>__chr_nvram_size</code>
|KiB of CHR-NVRAM
|-
|<code>__mirroring</code>
|Bit 0 of field 6
|-
|<code>__battery</code>
|Bit 1 of field 6
|-
|<code>__trainer</code>
|Bit 2 of field 6
|-
|<code>__four_screen</code>
|Bit 3 of field 6
|-
|<code>__mapper</code>
|Mapper number.
|-
|<code>__console_type</code>
|Low two bits of field 7
|-
|<code>__timing</code>
|Low two bits of field 12
|-
|<code>__ppu_type</code>
|Low nibble of byte 13 when Vs. System
|-
|<code>__hw_type</code>
|High nibble of byte 13 when Vs. System
|-
|<code>__extended_console_type</code>
|Low nibble of byte 13 when Extended Console Type
|-
|<code>__misc_roms</code>
|Low two bits of byte 14
|-
|<code>__default_expansion_device</code>
|Low six bits of byte 15
|-
|<code>__prg_rom_size_raw</code>
|The most significant nibble is the lower nibble of field 9, and the least significant byte is field 4. Overrides <code>__prg_rom_size</code>.
|-
|<code>__chr_rom_size_raw</code>
|The most significant nibble is the upper nibble of field 9, and the least significant byte is field 5. Overrides <code>__chr_rom_size</code>.
|-
|<code>__prg_ram_size_raw</code>
|Raw value of low 4 bits of header field 10. Overrides <code>__prg_ram_size</code>.
|-
|<code>__prg_nvram_size_raw</code>
|Raw value of high 4 bits of header field 10. Overrides <code>__prg_nvram_size</code>.
|-
|<code>__chr_ram_size_raw</code>
|Raw value of low 4 bits of header field 11. Overrides <code>__chr_ram_size</code>.
|-
|<code>__chr_nvram_size_raw</code>
|Raw value of high 4 bits of header field 11. Overrides <code>__chr_nvram_size</code>.
|}
== Linker layout ==
=== Sections ===
==== PRG-ROM ====
If PRG-ROM banking is disabled, then data can be placed in PRG-ROM using the regular <code>.rodata</code> C sections. Otherwise, data can be placed into a specific PRG-ROM bank using section <code>.prg_rom_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The load addresses of PRG-ROM banks begin at <code>0x01000000</code>. This allows the bottom 24-bits to represent a logical PRG-ROM address space, while the high byte being <code>0x01</code> indicates that the address is PRG-ROM.
==== CHR-ROM ====
If CHR-ROM banking is disabled, then data can be placed in CHR-ROM using the <code>.chr_rom</code> section or any section that begins with <code>.chr_rom.</code>. Otherwise, data can be placed into a specific CHR-ROM bank using section <code>.chr_rom_<bankno></code> or any section that begins with <code>.chr_rom_<bankno>.</code>. The logical addresses of CHR-ROM banks begins at <code>0x02000000</code> . This allows the bottom 24-bits to represent a logical CHR-ROM address space, while the highest byte being <code>0x02</code> indicates that the address is CHR-ROM.
==== PRG-RAM ====
If PRG-RAM banking is disabled, then data can be placed in PRG-RAM using the <code>.prg_ram</code> section or any section that begins with <code>.prg_ram.</code>. Otherwise, variables can be placed into a specific PRG-RAM bank using section <code>.prg_ram_<bankno></code> or any section that begins with <code>.prg_rom_<bankno>.</code>. The PRG-RAM is not initialized at program start, so any initializers given in C or data provided in assembly are ignored.
=== INCLUDE Configuration ===
In some cases, the linker script semantics are insufficiently powerful to configure the linker alone. In this case, linker scripts can be composed by `INCLUDE`-ing script files to build up a custom linker script (rather than using the default). The following script files are common to all targets.
You can either include these in your own projects linker script, or pass <code>-T''filename''</code> to the linker. For example:<syntaxhighlight lang="makefile">
# makefile
LDFLAGS = -lneslib -lnesdoug -Tcommon.ld -Tc-in-prg-ram.ld -Tprg-rom-banked-mode-0
</syntaxhighlight>
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>common.ld</code>
|Functionality common to all linker scripts for a given target. Must be included. Included in default linker script.
|-
|<code>c-in-ram.ld</code>
|Place the writable C sections (static/global variables and such) into NES RAM. Exactly one <code>c-in-</code> script must be included. Included in default linker script.
|}
=== ELF address space ===
On the NES target, the ELF address space is split into three sections:
{| class="wikitable"
|+NES target - LMA format
!Type (bits 31-24)
!Bits
!Name
!Values
|-
| rowspan="2" | <code>0x00</code> (CPU)
|23 - 16
|Bank
|<code>0x00</code> .. <code>0xFF</code>
|-
|15 - 0
|Address
|<code>0x0000</code> .. <code>0xFFFF</code>
Matches 16-bit address for the CPU
|-
| <code>0x01</code> (CHR)
|23 - 0
|Address
|<code>0x000000</code> .. <code>0xFFFFFF</code>
|}
== Mapper-specific information ==
=== [https://www.nesdev.org/wiki/NROM NROM], CNROM ===
==== INCLUDE Configuration ====
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|}
=== MMC1 ===
The 512KiB PRG-ROM mode and PRG-ROM bank modes other than 3 are not yet supported, since we don't yet have a general mechanism for copying arbitrary code into all banks to simulate a fixed bank at the end of the address space.
==== INCLUDE Configuration ====
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram-0.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked.ld</code>
|Up to 256KiB of banked PRG-ROM.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
=== MMC3 ===
By default, the mapper is configured such that 24KiB of PRG-ROM is fixed, while a 8KiB bank is available at 0x8000. This can be altered by INCLUDEing alternative linker scripts.
In banking mode 0, the even banks are available at 0x8000, and the odd banks are available at 0xa000. In banking mode 1, the even banks are available at 0xc000, and the odd banks at 0xa000. This assignment can be changed on a per-bank basis by defining the symbol <code>__prg_rom_NN</code>, where <code>NN</code> is the bank number.
==== INCLUDE Configuration ====
{| class="wikitable"
|+INCLUDE Files
!Name
!Description
|-
|<code>c-in-prg-ram.ld</code>
|Place the writable C sections (static/global variables and such) into PRG-RAM instead of system RAM.
|-
|<code>prg-rom-banked-8.ld</code>
|One mappable 8KiB bank at 0x8000, and 3 contiguous fixed banks (24 KiB total) starting at 0xa000.
|-
|<code>prg-rom-banked-mode-0.ld</code>
|Two separately mappable 8KiB banks at 0x8000 and 0xa000, and 2 contiguous fixed banks starting at 0xc000.
|-
|<code>prg-rom-banked-mode-1.ld</code>
|Two separately mappable 8KiB banks at 0xc000 and 0xa000, the next-to-last bank fixed at 0x8000, and the last bank fixed at 0xe000.
|-
|<code>prg-rom-fixed.ld</code>
|Up to 32KiB of fixed PRG-ROM.
|}
=== Action 53 ===
This mapper is the primary target for the NES Game Jam "NESDEV Compo." Because the common developer use case for this mapper is to make a ROM that's a part of a multi-cart, the defaults for this target are set for that purpose. The defaults are as follows: 64KiB PRG-ROM, 32KiB CHR-RAM, with 16KiB banking is available at 0x8000. This can be altered by changing the standard iNES configuration symbols, but adds another symbol <code>__supervisor_outer_bank</code> which will be ORed with the value written to $80 if you need to change the switchable bank to 0xc000.
397b33814ee8f0efbd02c9cd54a06a292d96cee8
Clangd
0
55
466
366
2023-09-06T07:45:22Z
Mysterymath
3
Call out that the asterisks are literal
wikitext
text/x-wiki
The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK.
The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though:
# Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory.
# Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd.
# Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. Note that the asterisks are literal; they function as wildcards. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers.
== CMake + Visual Studio Code ==
To set up clangd when building using CMake and editing with Visual Studio Code:
# Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd.
# Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators.
# Build the project.
# In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd."
# In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. Note that the asterisks are literal; they function as wildcards.
# Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path.
# Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install.
11c43171aa9adb60b5c17c73ae33bffad88fd2f4
468
466
2023-09-14T17:34:05Z
Cogwheel
13
Added a case-sensitivity warning for Windows users
wikitext
text/x-wiki
The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK.
The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though:
# Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory.
# Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd.
# Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. Note that the asterisks are literal; they function as wildcards. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers.
'''Note for Windows users''': the <code>query-driver</code> path is case-sensitive. Use a capital drive letter.
== CMake + Visual Studio Code ==
To set up clangd when building using CMake and editing with Visual Studio Code:
# Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd.
# Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators.
# Build the project.
# In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd."
# In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. Note that the asterisks are literal; they function as wildcards.
# Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path.
# Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install.
17f6cb4337d5d75b2a845036e060e5c6df723d81
470
468
2023-09-14T19:33:17Z
Cogwheel
13
further clarification for windows
wikitext
text/x-wiki
The SDK includes a build of the [https://clangd.llvm.org/ clangd] language server. This allows semantic features of code editors and IDEs that support the [[wikipedia:Language_Server_Protocol|Language Server Protocol]] to work with the SDK.
The specifics of setting up clangd depend on one's choice of editor and build system. There are three general rules, though:
# Make sure that your build system is generating a <code>compile_commands.json</code> file. Look for this in your build or output directory.
# Make sure that the editor is pointed at the clangd binary included with the SDK, not a system one. The clang included with the SDK supports language extensions that aren't in upstream clang, which can confuse upstream clangd.
# Set your editor so that the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code> is passed to clangd. Note that the asterisks are literal; they function as wildcards. This authorizes clangd to query the SDK clang about what path a specific SDK target uses. Without this, clangd won't have any idea where to look for target headers.
'''Note for Windows users''': the <code>query-driver</code> path is case-sensitive. Use a capital drive letter and backslash <code>\</code> for the path separator.
== CMake + Visual Studio Code ==
To set up clangd when building using CMake and editing with Visual Studio Code:
# Make sure that you've installed the Clangd extension in the Visual Studio Code marketplace. You should also disable the default C++ extension, since it conflicts with clangd.
# Pass <code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</code> to CMake when generating the build files. This ensures that a build produces a <code>compile_commands.json</code> file. Note that this only works with the Makefile and Ninja generators.
# Build the project.
# In Visual Studio Code, open your project, then go to the Settings page. Click the "Workspace" tab and search for "Clangd."
# In the setting titled "Clangd: Arguments", add the argument <code>--query-driver=/path/to/llvm-mos-sdk/bin/*clang*</code>, substituting the absolute path to your SDK installation for <code>/path/to/llvm-mos-sdk</code>. Note that the asterisks are literal; they function as wildcards.
# Set "Clangd: Path" to <code>/path/to/llvm-mos-sdk/bin/clangd</code>, again substituting your install path.
# Issue the command "clangd: Restart language server". Clangd integration should now work; you can test this by clicking "Go To Definition" on a <code>#include</code>line. This should take you to the corresponding header in your SDK install.
814f7f265b1d99ccfccbc2509642456b0b74931f
C compiler
0
18
467
265
2023-09-09T18:38:04Z
Asie
12
document defines
wikitext
text/x-wiki
A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Different CPUs may be selected using the <code>-mcpu</code> flag, for example <code>-mcpu=mosw65816</code>.
== Defines ==
The compiler defines the <code>__mos__</code> macro for the <code>mos</code> target. In addition, defines are added for every CPU which is compatible with the code; for instance <code>-mcpu=mos65c02</code> additionally defines <code>__mos6502__</code> and <code>__mos65c02__</code>.
== Known issues ==
The Clang is broadly compatible with the freestanding portion of the C99 standard, with one big caveat:
* No float and no double. We'll eventually ship a working IEEE 754 soft float library with the compiler for completeness' sake, but we expect low demand for this, and it'll distract from the rest of the project.
Some of the GCC and Clang language extensions work, some don't. Similarly some of the C++ language features work, some don't. We haven't exhaustively catalogued this; there's a lot, and some are extremely obscure and nearly useless.
Still, some compiler extensions are de-facto standard, so we'll call out a few caveats for those:
* The GCC/Clang alignment directives work only for global and static variables, not automatic (local) variables. Aligning the stack pointer is tricky, since it requires use of a frame pointer; but the rigors of 6502 indexing cause our frame pointer to be useless for this purpose. To get this to work, we may need *two* frame pointers. Expected call for this feature is low, so if you need it, let us know, and we'll reprioritize.
[[Category:C]]
7f08d28c0b00634a674bcb20f6b101434276af3c
Rationale
0
26
473
303
2023-09-16T06:11:55Z
Mysterymath
3
Remove out of date line about Rust and C++
wikitext
text/x-wiki
== Why yet another 6502 C compiler? ==
After all, there's [https://github.com/cc65/cc65 cc65], and [https://gitlab.com/camelot/kickc KickC], and [http://www.compilers.de/vbcc.html vbcc], and plenty of other efforts out there to target the 6502. And the processor is ancient anyway. So why bother porting LLVM to it?
== Performance ==
As an LLVM backend, we benefit from the expansive high-level optimizations available. These include radical code transformations of switch statements, loops, and table lookups. Nothing beyond what a human could do of course; a human wrote all these optimizations, of course. But there's potential far beyond what a human would have patience for. As an example, switch statement cases may be shifted and bitwise operations applied to them to make the different case integers denser. This increases the number that can fit into a jump table, which decreases the amount of branching needed to execute the switch. A human could do that for a switch statement, but it's unlikely they'd go through the effort for any but the most performance critical. LLVM will tirelessly consider it for every single switch in the program.
At a lower level, good use of the zero page is essential to producing good 6502 code. To that end, we model the zero page as an "imaginary register" bank. The placement of these registers are completely customizable by the end user to fit a variety of target system memory models. Using registers for this purpose allows us full access to LLVM's register allocator, which can often allocate program temporary values in such a way that they never need to leave the zero page, A, X, and Y. This vastly reduces need for soft (emulated) stack, which is a sticking point for earlier 6502 compilers.
Even when a stack of some kind is required, the optimizer performs whole-program analysis to identify functions that cannot simultaneously have more than one invocation active. These functions can have their "stack frames" allocated in absolute memory, again avoiding use of the soft stack. We reserve the actual soft stack only for cases where it cannot be statically proven that a function doesn't intrinsically require it (due to function pointers or other complex control flow).
As for the code itself, we perform a remarkably effective loop optimization that detects 16-bit index operations that can be converted to a 16-bit index plus an 8-bit offset. The latter is a directly-supported addressing mode on the 6502, and 8-bit index manipulation can be done in a single instruction. This allows us to convert idiomatic 16-bit "int c" loops into something much more suitable for the 6502. Eventually, we hope that optimizations of this kind will transform standard, naive C code into tightly optimized 6502 code.
== Features ==
Because this is a subproject of LLVM, it inherits all the features of LLVM. Namely, this project provides full ELF support for 6502 objects, libraries, and executables. This opens up previously impossible functionality, such as viewing 6502 program properties in ELF tools that don't know anything about the 6502 specifically.
llvm-mos is not limited to C. It also provides a fully functional assembler and disassembler that reads and writes assembly source files in a GNU assembler compatible format. This in turns opens up a world of macro programming functionality, for those who prefer to work at the metal level.
Lastly, the llvm-mos project is entirely open source, and developed entirely consistently with LLVM coding standards in mind. Want to experiment with a new codegen pass, or adding a new target? Jump right in, clone the codebase, and start playing.
See also [[Findings]].
[[Category:Main]]
7e4a8ae83efe64d65f54dda2c67ab01da4241940
Character set
0
62
475
453
2023-09-16T12:48:03Z
Asie
12
Prettify table, add cx16/atari8 charsets.
wikitext
text/x-wiki
Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code>) to target character sets.
With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced.
{| class="wikitable"
|+Supported Character Sets
!Target
!Name
!Syntax
!Notes
|-
| rowspan="6" | Commodore PET, Commodore 64, Commander X16
|Unshifted PETSCII
|<code>U"HELLO"_u</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted.
|-
|Shifted PETSCII
|<code>U"hello"_s</code>
|-
|Unshifted Video
|<code>U"HELLO"_uv</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|Shifted Video
|<code>U"hello"_sv</code>
|-
|Unshifted Reverse Video
|<code>U"HELLO"_urv</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these.
|-
|Shifted Reverse Video
|<code>U"HELLO"_srv</code>
|-
|Commander X16
|ISO-8859-15
|<code>U"Hello"_i</code>
|C0 and C1 Control codes are passed through uninterpreted. Corresponds to the "ISO mode".
|-
| rowspan="6" | Atari 8-bit
|ATASCII
|<code>U"Hello"_a</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted.
|-
|International ATASCII
|<code>U"hello"_i</code>
|-
|ATASCII Video
|<code>U"HELLO"_av</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|ATASCII International Video
|<code>U"hello"_iv</code>
|-
|ATASCII Reverse Video
|<code>U"HELLO"_arv</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character.
|-
|ATASCII International Reverse Video
|<code>U"HELLO"_irv</code>
|}
3330bba4e5144581692badccce14665d73702e3d
476
475
2023-09-16T12:48:21Z
Asie
12
wikitext
text/x-wiki
Due to limitations in clang, llvm-mos's execution character set is always UTF-8 with respect to the C standard. However, thanks to the magic of C++20 user-defined literals, we provide compile-translation from source C++ UTF-32 literals (i.e. <code>U""</code>) to target character sets.
With the acceptance of Unicode [[wikipedia:Symbols_for_Legacy_Computing|Symbols For Legacy Computing]] , there are now official mappings between Unicode code points and some 6502 target platform character sets, and we follow these rigorously whenever applicable. If the string cannot be completely mapped to the target character set, the compile error <code>no matching literal operator for call to ...</code> will be produced.
{| class="wikitable"
|+Supported Character Sets
!Target
!Name
!Syntax
!Notes
|-
| rowspan="6" | Commodore PET, Commodore 64, Commander X16
|Unshifted PETSCII
|<code>U"HELLO"_u</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted.
|-
|Shifted PETSCII
|<code>U"hello"_s</code>
|-
|Unshifted Video
|<code>U"HELLO"_uv</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|Shifted Video
|<code>U"hello"_sv</code>
|-
|Unshifted Reverse Video
|<code>U"HELLO"_urv</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character. Note that some Unicode characters are already "inverses"; the normal character is selected for these.
|-
|Shifted Reverse Video
|<code>U"HELLO"_srv</code>
|-
|Commander X16
|ISO-8859-15
|<code>U"Hello"_i</code>
|C0 and C1 Control codes are passed through uninterpreted. Corresponds to the "ISO mode".
|-
| rowspan="6" | Atari 8-bit
|ATASCII
|<code>U"Hello"_a</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted.
|-
|International ATASCII
|<code>U"Hello"_i</code>
|-
|ATASCII Video
|<code>U"Hello"_av</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM.
|-
|ATASCII International Video
|<code>U"Hello"_iv</code>
|-
|ATASCII Reverse Video
|<code>U"Hello"_arv</code>
| rowspan="2" | C0 Control codes are passed through uninterpreted. Corresponds to the character set's layout in ROM. Selects the inverse character for each Unicode character.
|-
|ATASCII International Reverse Video
|<code>U"Hello"_irv</code>
|}
11ea5914b639f730f5502b9a84b9a662640d5c92
Imaginary registers
0
42
477
294
2023-09-17T19:16:07Z
Mysterymath
3
Add an explicit description of imaginary register layout.
wikitext
text/x-wiki
The MOS 65xx series has a 256-byte range of low memory referred to as zero page. Memory stored in this region is significantly faster to access and modify.
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers.
Because "virtual registers" has another predefined meaning in LLVM land, we use the term '''imaginary register''' to refer to a byte in zero page memory. It's represented at codegen time by a symbol like __rc17 or __rs5, which is then translated to an actual memory address by the linker at link time.
Presently, the compiler requires 16 imaginary pointers (two contiguous bytes each). These are called rs0-rs15. Each is subdivided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0-rc31.
LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
88212f11a75eff6a417831240f0e75228def62ed
C Inline Assembly
0
40
478
378
2023-09-23T03:30:56Z
Jroweboy
14
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
[[Category:C]]
[[Category:Assembly]]
dfae2cc2ad51cff1a9244225e917727e7bf8de3c
Zero page
0
60
479
442
2023-09-25T02:55:37Z
Mysterymath
3
Add a blurb about zero page pointers.
wikitext
text/x-wiki
The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may reserving zero page memory from the compiler. Mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]].
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using e.g. <code>char __zp foo;</code>or <code>char __zeropage foo;</code> . The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler, and pointers to such variables can also be declared to be in the zero page, which allows them to fit in 8 bits. For example, <code>char __zp *fptr = &foo; assert(sizeof(fptr) == 1);</code> .
While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program.
[[Category:Code generation]]
[[Category:C]]
33020f53aba5e689767bd07d9da367ef44454c26
ELF specification
0
15
480
434
2023-09-26T14:58:27Z
Asie
12
0.3.0: SPC700 support, relocation type fix
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.0 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[[Category:Linking]]
aee6b4467a66811d0a7022d1a32f5f9c659a3e30
481
480
2023-09-26T15:02:19Z
Asie
12
add SPC-700 docs to references
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.0 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field should have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits should have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
70dc467999374c178d605cfaa23e7378d3f61e27
482
481
2023-10-09T21:14:39Z
Jbyrd
1
/* Machine type field */ Should to shall
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.0 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
8f5435fe78375e3d143af99c87aa41efb68bfea3
Modifiers
0
16
483
380
2023-10-12T06:45:58Z
Jroweboy
14
Try to fix the syntax being parsed as email
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar).
[[Category:Assembly]]
a206651555e4666ba44d36329fcb347e7cd62ae9
Welcome
0
1
484
450
2023-10-29T01:58:17Z
Jbyrd
1
Trivial edit to test editing
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing, public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
736e3cf6d3b6c816c85fcb5fc6f3bcc454feeb72
494
484
2023-12-24T21:15:07Z
Jbyrd
1
Trivial edit to test editing
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
The llvm-mos Clang is broadly compatible with freestanding C99 and C++ (with some notable exceptions) and the relevant portions of the LLVM end-to-end test suite pass on a simulated 6502 in a variety of configurations. The project also includes a feature-complete assembler and ELF linker support for generic 6502 targets.
This project permits modern C programs, written in a modern style, to target common microcomputers of the 1980s, including but not limited to the [[wikipedia:Commodore_64|Commodore 64]], the [[wikipedia:Apple_IIe|Apple IIe]], [[wikipedia:Atari_8-bit_family|Atari 8-bit family]], and the [[wikipedia:Nintendo_Entertainment_System|NES]].
Our work is based on LLVM's novel [https://llvm.org/docs/GlobalISel/index.html GlobalISel] architecture, and our compiler is ''aggressive'' about pursuing optimization opportunities for the 65xx series. While there's still much work to be done, we've already overcome the major theoretical hurdles necessary to emit high quality 6502 code.
Core development occurs on a [https://github.com/llvm-mos project on Github]. Acceptance tests and packaging occur via [https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions Github actions] as well.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation or LLVM project. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
73d010ff8df6da5c1c254ca2c0dbe09620b14a7d
496
494
2024-01-10T23:48:41Z
Jbyrd
1
Rewrite introduction with more of an overview
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
To get started playing with the tools, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started] with our SDK. See also the [https://llvm-mos.github.io/llvm-mos-sdk/files.html SDK API reference].
Our work encompasses the following:
* A fork of the LLVM project that provides first-class support to the MOS 6502 and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including IEEE-754 floating point. Our compiler implements several novel approaches in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented complete ELF support for the 6502, which permits us to use the existing suite of LLVM tools for creating, analyzing and modifying 6502-specific object files and libraries.
* An SDK project that comprises target-specific code for common 65xx-based microcomputers. It includes a minimal C standard library with support for memory management and text output. Some example programs are included. We currently support over two dozen targets, including a host-based simulator. The list of supported targets is growing rapidly.
* An automated test and packaging infrastructure. We use Github runners to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests. We also run automated benchmarks using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
31f18848839a605db796f19546321a1be57ec4d9
497
496
2024-01-11T00:34:34Z
Jbyrd
1
Added lots of links
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including IEEE-754 floating point. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo several novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing suite of LLVM tools for creating, analyzing and modifying 6502-specific object files and libraries.
* A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a minimal C standard library with support for memory management and text output. Some example programs are included. We currently support over two dozen targets, including a host-based simulator. The list of supported targets is growing rapidly.
* An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
eef1f6a33b9f8a55279f20df44ae5629813245bd
498
497
2024-01-11T00:38:29Z
Jbyrd
1
More links
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point]. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing suite of LLVM tools for creating, analyzing and modifying 6502-specific object files and libraries.
* A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some example programs are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a host-based simulator. The list of supported targets is growing rapidly.
* An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
794fc718c003cdc0569beda24338f57c6a4a9f7b
499
498
2024-01-11T00:41:27Z
Jbyrd
1
Yet more links
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point]. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries.
* A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
195014c6d378e3fa4080257815d15d9d8fdc81f6
500
499
2024-01-11T00:42:21Z
Jbyrd
1
Little more commentary
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries.
* A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own smoke tests. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
0ea2a1bae5e53c009f8e72506d424756f6bdf0bc
501
500
2024-01-11T00:43:05Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* A [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries.
* A [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
cc2294210f13fd0e75388435c5cce23245d4d293
502
501
2024-01-11T00:44:19Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
e4cb2970effb306016319e239ea7066f71ab0e12
503
502
2024-01-11T00:45:14Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502-specific object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
cb162de32a385aad663e24f44ecb0742478f5f60
504
503
2024-01-11T00:45:32Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point], as well as clang's world-class error messages. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
e645938ccead34e375b0e5723635b63758e67997
505
504
2024-01-11T00:57:29Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to, broad C99 and C++11 freestanding compatibility, including [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point]; first-class [[Assembler|integrated 65xx assembler support]]; and clang's world-class error messages. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
2d2b246a5662ee1a050cb426222131992e724361
506
505
2024-01-11T00:58:31Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
9ceacb4fa7e4845ec72a246d441c9e2d47d40f96
Cc65 integration
0
64
485
2023-10-29T02:19:19Z
Mysterymath
3
Add a page on cc65 integration
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments.
This feature cannot be used during a relocatable link (<code>-r</code>).
{| class="wikitable"
|+Caption text
|-
!Header text!!Header text
|-
|Example||Example
|-
|Example||Example
|-
|Example||Example
|-
|Example||Example
|-
|Example||Example
|-
|Example||Example
|-
|Example||Example
|}
27d9432ee20b0078f4cf118441094efa169768e9
486
485
2023-10-29T02:22:10Z
Mysterymath
3
Fill out escape sequence table
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments.
This feature cannot be used during a relocatable link (<code>-r</code>).
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
2c7048559937915a9a3a66646b79a36b3558d12e
487
486
2023-10-29T02:23:45Z
Mysterymath
3
Add hex encoding
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments.
This feature cannot be used during a relocatable link (<code>-r</code>).
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_XX</code>
|The byte given by hex string <code>XX</code> (case insensitive)
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
96e2c0333a0bf4c2c9b84ed9a871d4f61e6dc949
488
487
2023-10-29T02:25:52Z
Mysterymath
3
Move to C and linking categories
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments.
This feature cannot be used during a relocatable link (<code>-r</code>).
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_XX</code>
|The byte given by hex string <code>XX</code> (case insensitive)
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
[[Category:C]]
[[Category:Linking]]
778a5e1e32960e9d9c26d773a25f622d9b7d210b
490
488
2023-10-30T05:18:03Z
Mysterymath
3
Mention that cc65 default segments are automatically placed
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names automatically. The default cc65 segments already the correct flags; this is only needed for adding to LLVM-MOS sections or defining custom segments. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section.
This feature cannot be used during a relocatable link (<code>-r</code>).
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_XX</code>
|The byte given by hex string <code>XX</code> (case insensitive)
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
[[Category:C]]
[[Category:Linking]]
6b170b552e9fd586c4f3bddfcde6b364a6bc6b9a
491
490
2023-10-30T05:56:22Z
172.69.135.138
0
Copy edit
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section.
This feature cannot be used during a relocatable link (<code>-r</code>), since the output is a relocatable ELF file, and ca65's relocations are much richer than LLVM-MOS ELF allows.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names. The default cc65 segments are automatically assigned the correct type and flags.
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_XX</code>
|The byte given by hex string <code>XX</code> (case insensitive)
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
[[Category:C]]
[[Category:Linking]]
2d6749e2e83892a5ee32d91ac2a9b3a0821c7c11
492
491
2023-11-02T16:39:46Z
Mysterymath
3
Update hex escape syntax
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section.
This feature cannot be used during a relocatable link (<code>-r</code>), since the output is a relocatable ELF file, and ca65's relocations are much richer than LLVM-MOS ELF allows.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names. The default cc65 segments are automatically assigned the correct type and flags.
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_xXX</code>
|The byte given by hex string <code>XX</code> (case insensitive)
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
[[Category:C]]
[[Category:Linking]]
5bf6b14b6f6500f71a6719f7ca293f478f1894b2
C interrupts
0
41
489
349
2023-10-29T03:56:16Z
Mysterymath
3
Update incorrect register save/restore sequence
wikitext
text/x-wiki
== Normal C Interrupt Handling ==
The techniques llvm-mos uses for interrupt handling are somewhat unusual. To understand why, it's useful to start with the normal way C compilers deal with interrupts.
Generally, C calling conventions divide registers into two classes: caller-saved and callee-saved. Functions are free to overwrite caller-saved registers, and callers of those functions need to be aware of and correctly handle this possibility. Functions must preserve the values of callee-saved registers, and their callers are allowed to count on this.
Interrupt handlers necessarily break with this convention; a function can't know when and how it's going to be interrupted, so there's no way to "deal with" the interrupt handler overwriting caller-saved registers. So interrupt handlers need to treat all registers as if they were callee-saved.
Usually, a target has at most around 32 registers, more-or-less evenly split between caller-saved and callee-saved. So an interrupt handler can reasonably save all the caller-saved registers (it would implicitly also save the callee-saved registers if it uses them, just by virtue of being a C function). Note that if it calls any other function, it needs to save ''all'' of the caller-saved registers, since it can't know which the callee will overwrite.
== Challenges ==
The nature of the 6502 presents some challenges to using this model.
=== Static Stack Allocation ===
The indexed addressing modes on the 6502 are quite slow, which gives incentive to avoid the normal C stack implementation. For llvm-mos, we opted to perform a call graph analysis and allocate the stack frames of non-recursive functions statically. This is safe, since via a conservative analysis we can prove that certain functions cannot have more than one invocation active at a time.
However, interrupts can be active at the same time as any other function, potentially including themselves. The static stack analysis will need to be made aware of interrupts somehow, which means that programmers will need to annotate which functions have this "can appear out of nowhere" property.
== Interrupt Annotations ==
To solve the above problems, we introduce three new function attributes "interrupt", "interrupt_norecurse", and "no_isr".
=== "interrupt" Attribute ===
This attribute isn't actually MOS-specific, but we do ascribe to it some additional semantics. Any function bearing this attribute will treat all registers (except flags) as callee-saved, will begin with a CLD (the state of the decimal flag is undefined upon interrupt), and will return with RTI instead of RTS. Additionally, any such a function will be marked as possibly recursive for the purpose of static stack allocation. This will in turn cause any function that might be called by such a function to be forced to use the dynamic soft stack.
=== "interrupt_norecurse" Attribute ===
This attribute behaves identically to "interrupt", with one exception. When performing the static stack analysis, functions marked with this attribute will not be automatically considered recursive. Instead, any functions that might possibly called by an interrupt_norecurse function and main or two ''different'' interrupt_norecurse functions will be considered possibly recursive. Another way of looking at it is that interrupt_norecurse functions correspond to different sources of interrupts, and the model is one where interrupts from that source are disabled until they finish processing. Judicious use of interrupt_norecurse allows interrupt handlers to benefit from static stack allocation, leaving the "interrupt" attribute for interrupt handlers that can interrupt even themselves.
=== "no_isr" Attribute ===
This attribute can be added to an interrupt or interrupt_norecurse function to cause it to return with RTS and not perform any of the additional saving that interrupt handlers usually do. All effects on static stack analysis and calling conventions (see below) still occur. The intent is to allow interrupt handlers to be implemented in assembly and call into C with the normal calling convention. The interrupt attributes would still be necessary in that case for program correctness.
==== Manual Interrupt Sequence ====
Some operating systems may impose unusual requirements on interrupt handlers. They may, for example, push certain registers before calling the handler, but expect the handler to pop them before returning. no_isr routines allow implementing this kind of custom interrupt prologue and epilogue. However, doing this safely requires saving and restoring anything that might be in-use by the compiler.
Below is a sample routine that includes the sum total of all pushes and pops needed by the compiler. If it can be proven that the entire, transitively called interrupt handler cannot use certain locations, then they can be elided. This is typically only possible if the interrupt handler is written entirely in assembly.
The callee-saved registers must be preserved by ''any'' function callable by C; interrupt handlers are no different. Accordingly, callee-saved registers aren't included in the code below. In this example, the JSR to "body" is expected to preserve them.<syntaxhighlight lang="6502tasm">
cld
pha
txa
pha
tya
pha
lda mos8(__rc2)
pha
lda mos8(__rc3)
pha
lda mos8(__rc4)
pha
lda mos8(__rc5)
pha
lda mos8(__rc6)
pha
lda mos8(__rc7)
pha
lda mos8(__rc8)
pha
lda mos8(__rc9)
pha
lda mos8(__rc10)
pha
lda mos8(__rc11)
pha
lda mos8(__rc12)
pha
lda mos8(__rc13)
pha
lda mos8(__rc14)
pha
lda mos8(__rc15)
pha
lda mos8(__rc16)
pha
lda mos8(__rc17)
pha
lda mos8(__rc18)
pha
lda mos8(__rc19)
pha
JSR body
pla
sta mos8(__rc19)
pla
sta mos8(__rc18)
pla
sta mos8(__rc17)
pla
sta mos8(__rc16)
pla
sta mos8(__rc15)
pla
sta mos8(__rc14)
pla
sta mos8(__rc13)
pla
sta mos8(__rc12)
pla
sta mos8(__rc11)
pla
sta mos8(__rc10)
pla
sta mos8(__rc9)
pla
sta mos8(__rc8)
pla
sta mos8(__rc7)
pla
sta mos8(__rc6)
pla
sta mos8(__rc5)
pla
sta mos8(__rc4)
pla
sta mos8(__rc3)
pla
sta mos8(__rc2)
pla
tay
pla
tax
pla
rti
</syntaxhighlight>
=== Undefined Behavior ===
It shall be undefined behavior for any mechanism external to a C module to asynchronously call a C function that does not bear either the "interrupt" or "interrupt_norecurse" attributes. It shall also be undefined behavior for any mechanism external to a C module to asynchronously call an "interrupt_norecurse" function while another invocation of that same function is still active.
[[Category:C]]
7622af06b1707fd7a96d7cceb0e4ca9d6fbb94b0
Building llvm-mos on MacOS
0
28
495
181
2024-01-04T09:01:08Z
Wombat
9
Add link to llvm building video
wikitext
text/x-wiki
The Macintosh build of LLVM is much more forgiving than the Windows build. With the Xcode command-line build tools installed:
<cmake ../../llvm -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_ENABLE_PROJECTS="clang;lld" -DLIBXML2_LIBRARIES=IGNORE -DCMAKE_INSTALL_PREFIX=${WORKSPACE}/build/stage2/install -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=all
cmake --build . --config Release --target check-all
cmake --build . --config Release --target install</code>
There is also a [https://www.youtube.com/embed/CGX96RmmlLQ?si=12HOY3id7mOhXXBS video walk-through] for MacOS and Linux.
[[Category:Building]]
6b78e4be184ee2483b249a4f2929af2d760b9b06
Welcome
0
1
507
506
2024-01-11T01:00:30Z
Jbyrd
1
/* Welcome to the llvm-mos project! */
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. This includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. We have implemented [https://llvm-mos.org/wiki/ELF_specification complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
54df9ea6b91f6707b15b0892d40b87a2862b6fbc
508
507
2024-01-11T01:01:51Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
6fecc9bada27e216b237d64924916d0eb39ffdc1
513
508
2024-01-29T18:31:18Z
Jbyrd
1
Reorder list
wikitext
text/x-wiki
[[Category:Main]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
19fec060818facb9c4dd7329787ab8e5c3b94c92
Ca65 comparison
0
65
509
2024-01-24T05:01:52Z
Mysterymath
3
Created page with "This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]]. ; ca65 .segment "PRG_ROM_1" ; llvm-mos .section .prg_rom_1 ; ca65 .global _score _score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code ; llvm-mos .global score score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here ; ca65 bn..."
wikitext
text/x-wiki
This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]].
; ca65
.segment "PRG_ROM_1"
; llvm-mos
.section .prg_rom_1
; ca65
.global _score
_score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code
; llvm-mos
.global score
score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here
; ca65
bne :+ ; add '+'s to jump to the next ':'s, or use '-'s to jump to previous ':'s
inc counter
:
; llvm-mos
bne 1f ; 'f' means the next label '1' forward from here, 'b' would look backwards instead, can use other numbers for different local labels
inc counter
1:
d97164126c56096581c7c7cc5c3358cd09d5dcab
510
509
2024-01-24T05:02:36Z
Mysterymath
3
Formatting
wikitext
text/x-wiki
This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]].
; ca65
.segment "PRG_ROM_1"
; llvm-mos
.section .prg_rom_1
; ca65
.global _score
_score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code
; llvm-mos
.global score
score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here
; ca65
bne :+ ; add '+'s to jump to the next ':'s, or use '-'s to jump to previous ':'s
inc counter
:
; llvm-mos
bne 1f ; 'f' means the next label '1' forward from here, 'b' would look backwards instead
; can use other numbers for different local labels
inc counter
1:
bd3c5b69a84d06e08d8b9f4b56e15cf3c79e22fd
511
510
2024-01-24T05:03:16Z
Mysterymath
3
wikitext
text/x-wiki
This page contains a "Rosetta Stone" for learning llvm-mos's GNU Assembler syntax by highlighting the differences from ca65. It's also possible to [[Cc65 integration|use ca65 directly]].
; ca65
.segment "PRG_ROM_1"
; llvm-mos
.section .prg_rom_1
; ca65
.global _score
_score: .res 5 ; we added a '_' so the variable can be used as 'score' on C code
; llvm-mos
.global score
score: .fill 5 ; 'score' is usable on C code, no need for a '_' prefix here
; ca65
bne :+ ; add '+'s to jump to the next ':'s, or use '-'s to jump to previous ':'s
inc counter
:
; llvm-mos
bne 1f ; 'f' means the next label '1' forward from here, 'b' would look backwards instead
; can use other numbers for different local labels
inc counter
1:
[[Category:Assembly]]
e289df0d246eaba80a0dbac13fe83f11ddbcb097
Porting
0
47
512
331
2024-01-26T21:21:34Z
172.71.151.117
0
minimal ld script needed updating
wikitext
text/x-wiki
We've designed the LLVM-MOS SDK to be as possible to port to new platforms (but no easier). This is a tutorial-style guide on how to do so.
== Imaginary Target ==
For the purposes of this guide, we'll need a target to port to. Rather than use a real target (which may have an official port by the next time this guide is updated), we'll invent a new one.
We'll make the target as simple as possible. (Real targets are complicated, but they're all complicated in different ways). Let's say the target has 64KiB of RAM available, with no banking. We'll also imagine that the target has an emulator that's capable of loading programs in some file format. Let's say the file format is very simple: a 64KiB image to load into RAM, followed by two bytes indicating the start address, little-endian.
== The Simplest Program ==
First, make sure the latest SDK release is extracted somewhere, and make a directory to work in. You can do most of this tutorial without the SDK sources; you only need the SDK sources if you're looking to contribute your port to the SDK. (But please do!)
Next, create the simplest possible C program: <code>main.c</code><syntaxhighlight lang="c">
int main(void) { return 0; }
</syntaxhighlight>
== Parent Target ==
The SDK's targets are hierarchical: a target can have an ''incomplete target'' as a parent. The parent is called ''incomplete'' because the child fills in missing pieces of it. An incomplete target can also have a different incomplete target as a parent, forming a tree. The ''complete'' targets form the leaves of this tree; only these can produce binaries.
For porting a real target, take a look at the SDK; there may already be an incomplete target for the family of devices or boards you're porting to. Completing an incomplete target is much much easier than building one from scratch.
Along those lines, the tree of targets is rooted at the <code>common</code> target. This provides functionality that is essential to running C on a 6502; it can be reasonably shared by all targets.
Since we're porting to fake target, we should select <code>common</code> as our parent target. This means to compile our code, we should invoke <code>clang</code> as <code>mos-common-clang</code>.
== Compiling: First Attempt ==
Let's compile:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
ld.lld: error: cannot find linker script link.ld
</syntaxhighlight>This fails because the linker has no earthly idea how to layout out code for our platform. For this, we have to provide a linker script, <code>link.ld</code>.
== Linker Script ==
The linker scripts are based on GCC linker scripts ([https://sourceware.org/binutils/docs/ld/ reference]), which is extended by LLD ([https://lld.llvm.org/ELF/linker_script.html reference]), which is further extended by LLVM-MOS ([[Linker Script|reference]]).
There's a lot of functionality packed behind these little scripts; it can take time to learn the language thoroughly. However, you don't need very much to get started.
Here's a minimal linker script for our platform: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
zp : ORIGIN = __rc31 + 1, LENGTH = 0x100 - (__rc31 + 1)
ram (rw) : ORIGIN = 0x200, LENGTH = 0xfe00
}
REGION_ALIAS("c_readonly", ram)
REGION_ALIAS("c_writeable", ram)
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
</syntaxhighlight>The <code>MEMORY</code> section describes the layout of the RAM available for the linker to put linked sections in. This typically excludes the zero page and stack; these are usually handled by other mechanisms. This <code>MEMORY</code> section states that there's a memory region named <code>ram</code> suitable to be assigned both read-only and writable sections. It starts at 0x200, and it ends at the end of RAM.
The <code>SECTIONS</code> directive states which sections from input files the linker should place in which output sections, as well as symbols relating to section placement. The linker will automatically place all sections in the <code>ram</code> region, which is what we want.
The next bit of linker script assigns symbols <code>__rc0</code> through <code>__rc31</code> to addresses <code>0</code> through <code>31</code>. This defines the "imaginary registers" in the zero page that are reserved for compiler use (and that form the C calling convention). <code>INCLUDE imag-regs.ld</code> is a helper script that automatically assigns each unset register to the register before it + 1. Thus, you only need to set the first register to zero, and the script takes care of the rest. Note that you can only specify the locations of even registers; the odd registers are fixed, since they must immediately follow the preceding register for the pair to work as a pointer.
== Compiling: Second Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
...
$ file main
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
</syntaxhighlight>That compiled, but it's an ELF file, which isn't at all what the target takes. LLVM-MOS uses ELF for its object files, libraries, and executables (by default). ELF provides rich information about the contents; this is what allows the full suite of LLVM tools to work. For example, you can use <code>llvm-nm</code> to dump all the symbols in the generated file:<syntaxhighlight lang="shell-session">
$ llvm-nm main
00000209 B __bss_end
00000000 A __bss_size
00000209 B __bss_start
00000209 T __data_end
00000209 A __data_load_start
00000000 A __data_size
00000209 T __data_start
00000209 T __fini_array_end
00000209 T __fini_array_start
00000209 B __heap_start
00000209 T __init_array_end
00000209 T __init_array_start
00000000 A __rc0
00000001 A __rc1
...
00000009 A __rc9
00000203 T _fini
00000200 T _init
00000200 T _start
00000204 T main
</syntaxhighlight>Most of these symbols were generated by the <code>INCLUDE c.ld</code> call, but you can also see our <code>main</code> function was placed at 0x204, and that the imaginary registers were set up appropriately.
You can get a disassembly too:<syntaxhighlight lang="shell-session">
$ llvm-objdump -d --print-imm-hex main
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>However, none of this helps make an output file that our emulator can actually load.
== Output Format ==
To make our object file, we return to our linker script: <code>link.ld</code><syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>The new line, <code>OUTPUT_FORMAT { FULL(ram) SHORT(_start) }</code>, is an LLVM-MOS extension to the linker script language that describes what our actual output file should look like. We want the full contents of RAM to be output, padded with zeros. Afterwards, we want a little-endian short containing the starting point of the program, set up by the <code>common</code> target: <code>_start</code>.
Additionally, the <code>ram</code> section was renamed to <code>user_ram</code>; this is more appropriate, since it doesn't actually span the full 64KiB address range, which is what we want to write to the file. Instead, we create an overlapping <code>ram</code> section that starts at 0 and ends at 0xffff. This is the section we write in the <code>OUTPUT_FORMAT</code>.
== Compiling: Third Attempt ==
<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c
$ ls -l
...
main
main.elf
...
$ file main
main:data
$ file main.elf
main: ELF 32-bit LSB executable, *unknown arch 0x1966* version 1 (SYSV), statically linked, not stripped
$ hexdump -C main
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 20 04 02 60 a2 00 a9 00 60 00 00 00 00 00 00 00 | ..`....`.......|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 02 |..|
00010002
</syntaxhighlight>This time two files were produced: <code>main</code>, and <code>main.elf</code>. <code>main.elf</code> is the ELF file produced previously, while <code>main</code> contains the contents as described by the <code>OUTPUT_FORMAT</code> script.
Looking at the contents of <code>main</code>, we have 64KiB of data, with the interesting stuff beginning at 0x200, as expected. Afterwards, we have <code>00 02</code>, which is the little-endian word 0x200, which is indeed the value of <code>_start</code>.
== Optional Libraries ==
Take another look at the disassembly:<syntaxhighlight lang="shell-session">
main: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 04 02 jsr $204
00000203 <_fini>:
203: 60 rts
00000204 <main>:
204: a2 00 ldx #$0
206: a9 00 lda #$0
208: 60 rts
</syntaxhighlight>Several things are amiss already. There's a <code>_start</code> routine that contains <code>JSR main</code>, that's good. But <code>_fini</code> doesn't look hooked up to anything. And once it's finished, <code>main</code> just returns back to <code>_start</code>, which runs right into <code>_fini</code>, which returns into never-never land.
This is all because the <code>common</code> target's libraries don't by default include any functionality that isn't absolutely necessary for the C runtime. In this case, that's <code>_start</code>, which contains pre-main functionality (e.g., <code>__attribute__((constructor))</code>) and C++ constructors, and <code>_fini</code>, which contains post-main functionality (e.g., <code>__attribute__((destructor))</code> and C++ destructors).
Other functionality is contained in ''optional libraries'' that must be explicitly included by the targets. This is either because the nature of the target and it's OS may make the functionality unnecessary, or because there is more than one best way to accomplish it, again depending on the target.
The canonical reference for these optional libraries are the various CMakeLists.txt files in the [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common common target].
One of the things that a target needs to decide is how a program is exited. This happens whenever <code>exit</code> is called, or implicitly when <code>main</code> returns. When either occurs, <code>_fini</code> must be called first. The <code>common</code> target provides optional libraries corresponding to the usual possibilities:
{| class="wikitable"
!Exit Library
!Description
|-
|<code>exit-custom</code>
|Run some custom code (e.g., execute a special BRK, which goes into an underlying OS).
|-
|<code>exit-loop</code>
|Go into an infinite loop.
|-
|<code>exit-return</code>
|Return from <code>_start</code>
|}
Our target doesn't have an underlying OS, so <code>exit-loop</code> seems reasonable. Specifying this library on the command line produces a more sensible behavior:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Now, after <code>main</code> returns back to <code>_start</code>, <code>_start</code> falls through to <code>__after_main</code>, which jumps to <code>exit</code>. <code>exit</code> in turn calls <code>_fini</code> then enters an infinite loop, as desired. <code>exit</code> can then be called by any C function to run <code>_fini</code> and loop, not just by returning from <code>main</code>.
There are some other optional libraries that are necessary to get full C language functionality on our target. Notably, we need to set up the stack. The <code>init-stack</code> optional library can do this; we just need to provide a symbol, <code>__stack</code>, that is the top of stack at program start. Since there's nothing on the stack, for this target the top of stack should actually be 0x10000; since that's not a real address should counterintuitively be 0. You could also use 0xffff and waste a byte; no-one would judge you.
We can set this in our linker script, <code>link.ld</code>:<syntaxhighlight lang="text">
MEMORY {
ram : ORIGIN = 0x0000, LENGTH = 0x10000
user_ram (rw) : ORIGIN = 0x0200, LENGTH = 0xfe00
}
SECTIONS { INCLUDE c.ld }
__rc0 = 0x00;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x00, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
/* Top of stack would be 0x10000, but wrap around. */
__stack = 0;
OUTPUT_FORMAT { FULL(ram) SHORT(_start) }
</syntaxhighlight>Finally, we can compile again and link against <code>init-stack</code> too:<syntaxhighlight lang="shell-session">
$ mos-common-clang -o main -Os main.c -lexit-loop -linit-stack
$ llvm-objdump -d --print-imm-hex main.elf
main.elf: file format elf32-mos
Disassembly of section .text:
00000200 <_start>:
200: 20 07 02 jsr $207
00000203 <__after_main>:
203: 4c 0c 02 jmp $20c
00000206 <_fini>:
206: 60 rts
00000207 <main>:
207: a2 00 ldx #$0
209: a9 00 lda #$0
20b: 60 rts
0000020c <exit>:
20c: 20 06 02 jsr $206
20f: 4c 0f 02 jmp $20f
</syntaxhighlight>Nothing changed! Is there a problem? Nope!
Since the C program doesn't actually use a stack, the linker doesn't include any code to set it up. That's the purpose of <code>_start</code> and <code>_fini</code>; these sections collect snippets of code to set things up, depending on what's actually used in the final binary. So, with this change, the target is complete for freestanding C/C++, and it even has an <code>exit</code> routine.'
= Extending the SDK =
After porting LLVM-MOS to a platform, please consider submitting your port for inclusion in the LLVM-MOS SDK. We'd like the SDK to be a nice out-of-the-box way to write code for any 6502 platform, and contributions along those lines are greatly appreciated.
To do so, continue onward to the [[Extending SDK]] guide.
a7c0abc7b9f7170d29a595182bbd30f64c53044c
C Inline Assembly
0
40
514
478
2024-01-31T21:16:09Z
Jbyrd
1
You can use mos-clang as a compiler
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
asm volatile ("t%0a" :: "d"(c));
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
1eba097da80998d01546e6c1f49687e2610306a4
519
514
2024-03-04T11:55:50Z
Wombat
9
Slightly more verbose and add missing A clobber
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> constrained operand to <code>A</code>, you can use the following:
unsigned char c = 1;
asm volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
8f2dc5a6eade6eccdf014196527bcb5156ddad9c
520
519
2024-03-04T11:59:23Z
Wombat
9
Add link to GAS manual for constrained operands
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following:
unsigned char c = 1;
asm volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
This will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>.
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
0455a2a7fb7411753e50154f5dbc1ca8326d74ae
521
520
2024-03-04T13:14:45Z
Wombat
9
Use syntax highlighted code block
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>:
<syntaxhighlight lang="c">
unsigned char c = 1;
asm volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
</syntaxhighlight>
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
4167d8d35513ed97a842753381b94dfb7025571a
544
521
2024-08-25T16:35:26Z
Wombat
9
Add leaf attribute and use `__asm__` over `asm`.
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit or 16-bit imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>:
<syntaxhighlight lang="c">
unsigned char c = 1;
__attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
</syntaxhighlight>
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
2a298e659c3a1bb1d5a840d813036b81560bee63
545
544
2024-08-27T14:46:12Z
Wombat
9
Add info about "p" clobber and 8/16 but operands
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit (<code>__rcxx</code>) or 16-bit (<code>__rsxx</code>) imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>:
<syntaxhighlight lang="c">
unsigned char c = 1;
__attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
</syntaxhighlight>
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list with e.g. <code>"c"</code>, <code>"v"</code>, or <code>"p"</code>.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
47a6ca25727732efa46098cafdedabc7bbbe3709
549
545
2024-10-22T20:08:16Z
Jbyrd
1
Added note on compiler crashes with misuse
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit (<code>__rcxx</code>) or 16-bit (<code>__rsxx</code>) imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
These modifiers are highly case-sensitive, and violating constraint use in inline assembly can [https://github.com/llvm-mos/llvm-mos/issues/463 crash the compiler], so use with a high degree of caution.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>:
<syntaxhighlight lang="c">
unsigned char c = 1;
__attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
</syntaxhighlight>
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list with e.g. <code>"c"</code>, <code>"v"</code>, or <code>"p"</code>.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
a024213f07c0aa47b49f3ac259dd7a454e54daff
554
549
2024-10-22T20:25:11Z
Jbyrd
1
wikitext
text/x-wiki
Clang supports most of the standard [https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C GCC inline assembly syntax]. There are a few notable exceptions:
* Unsupported Constraints: <code>o</code>, <code>V</code>, <code><</code>, <code>></code>, and <code>g</code>
* Goto Labels are unsupported.
"General purpose registers" for the purposes of inline assembly are taken to be 8-bit (<code>__rcxx</code>) or 16-bit (<code>__rsxx</code>) imaginary registers, depending on the size of the operand.
The compiler also supports a few MOS-specific constraints:
* <code>a</code>: The <code>A</code> register.
* <code>x</code>: The <code>X</code> register.
* <code>y</code>: The <code>Y</code> register.
* <code>R</code>: One of <code>A</code>, <code>X</code>, or <code>Y</code>.
* <code>d</code>: Either <code>X</code> or <code>Y</code>.
* <code>c</code>: The <code>C</code> flag.
* <code>v</code>: The <code>V</code> flag.
These modifiers are case-sensitive, and violating constraint use in inline assembly can [https://github.com/llvm-mos/llvm-mos/issues/463 crash the compiler], so use with a high degree of caution.
The unique opcode syntax of the 6502 allows using these operands in unorthodox ways. For example, to issue a copy from a <code>d</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constrained operand] to <code>A</code>, you can use the following which will issue either <code>txa</code> or <code>tya</code>, depending on where the compiler places <code>c</code>:
<syntaxhighlight lang="c">
unsigned char c = 1;
__attribute__((leaf)) __asm__ volatile ("t%0a" : /* no output */ : /* input */ "d"(c) : /* clobbers */ "a");
</syntaxhighlight>
Using GCC-style inline assembly has a lot of sharp edges and footguns, so beware. For a detailed guide on how to use this feature well, see: [[Frequently asked questions#Why is inline assembly behaving so strangely?]]
One more thing to note: the compiler tracks the contents of C and V (but not N and Z). Accordingly, if the inline assembly fragment can clobber C or V, they must be added to the clobber or output register list with e.g. <code>"c"</code>, <code>"v"</code>, or <code>"p"</code>.
If you're writing a lot of assembly, note that mos-clang works well as a frontend to the MOS assembler. You can put your labels and assembly code in its own .s file, and compile it with something like:
mos-clang -c my-assembly-file.s
[[Category:C]]
[[Category:Assembly]]
5257a236f123a31f9c35ec75c11f82ea934ba609
Community
0
66
515
2024-02-09T16:58:49Z
Mysterymath
3
Add a community links page
wikitext
text/x-wiki
llvm-mos has a vibrant community both officially and unofficially affiliated with it. The main official hub is our [https://discord.gg/D9gRm3aznh Discord]; drop by if you have questions or would like to contribute.
There are also a number of other projects loosely affiliated with llvm-mos; here are links to a few.
{| class="wikitable"
|+
!Site
!Description
|-
|[https://github.com/MEGA65/mega65-libc mega65-libc]
|C library for the Mega65. More thorough platform-specific code than present for the Mega65 llvm-mos SDK. Works with CC65, Clang, and KickC.
|-
|[https://mysterymath.neocities.org mysterymath's blog]
|Current llvm-mos maintainer's blog. llvm-mos internals discussed often at a very thorough level of detail.
|-
|[https://github.com/mrk-its/rust-mos rust-mos]
|Rust frontend port based on llvm-mos. Usually trails somewhat behind llvm-mos's releases.
|}
3317bc170ffdbcd882817e27e3dfe3446b94501d
516
515
2024-02-09T16:59:53Z
Mysterymath
3
Add to main category
wikitext
text/x-wiki
llvm-mos has a vibrant community both officially and unofficially affiliated with it. The main official hub is our [https://discord.gg/D9gRm3aznh Discord]; drop by if you have questions or would like to contribute.
There are also a number of other projects loosely affiliated with llvm-mos; here are links to a few.
{| class="wikitable"
|+
!Site
!Description
|-
|[https://github.com/MEGA65/mega65-libc mega65-libc]
|C library for the Mega65. More thorough platform-specific code than present for the Mega65 llvm-mos SDK. Works with CC65, Clang, and KickC.
|-
|[https://mysterymath.neocities.org mysterymath's blog]
|Current llvm-mos maintainer's blog. llvm-mos internals discussed often at a very thorough level of detail.
|-
|[https://github.com/mrk-its/rust-mos rust-mos]
|Rust frontend port based on llvm-mos. Usually trails somewhat behind llvm-mos's releases.
|}
[[Category:Main]]
40ade97b72d150f8b49509d8920bc1c61547a5ec
Frequently asked questions
0
49
517
433
2024-02-11T00:25:29Z
Mysterymath
3
Add an entry about 24-bit integers.
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) inline assembly feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234"::"R"(value));
}
</syntaxhighlight>The <code>R</code> constraint on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
cbfee43ea48862a4d70e813d967886c9165d0d35
522
517
2024-03-04T13:41:03Z
Wombat
9
Expanded comment on example and added internal link to inline assembly page
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [https://gcc.gnu.org/onlinedocs/gcc/Constraints.html constraint] on the <code>value</code> input specification says "put this in A, X, or Y". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
f06d4f6567a477e98561dfd34d69fe6cde9281df
523
522
2024-03-04T13:44:57Z
Wombat
9
Syntax highlighting
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234");
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
56b03366757b5767ed09c66ffd0ea6a151012e8f
546
523
2024-08-27T15:29:22Z
Wombat
9
Add comment to code snippet
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile`
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
7571e550d4a228029dccbd5d4774b3792a2e4591
547
546
2024-08-27T15:32:30Z
Wombat
9
/* Rule 2: Inputs, outputs, and volatile. */
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly behaving so strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile` and input operand
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
60f50116d7c1bf063c0e07c79d4d6748102a51ef
550
547
2024-10-22T20:12:51Z
Jbyrd
1
Warn about register modifiers
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly crashing the compiler or behaving strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile` and input operand
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
Rule 4. Suspect your register modifiers.
Register modifiers must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
2e1a3cf62bdff89f949b8935415378d145e2714a
551
550
2024-10-22T20:22:39Z
Jbyrd
1
Added link to assembly constraint violation information
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly crashing the compiler or behaving strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile` and input operand
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
=== Rule 4. Suspect your register modifiers. ===
Register modifiers must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
ed59fd1e40ee77e96db038f7dd51e65edea9b9ce
552
551
2024-10-22T20:23:20Z
Jbyrd
1
/* Rule 4. Suspect your register modifiers. */
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly crashing the compiler or behaving strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile` and input operand
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
=== Rule 4. Suspect your register constraints. ===
Register constraints must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
18d299e1de340d486996ae90352a63401e5dca28
553
552
2024-10-22T20:24:11Z
Jbyrd
1
/* Rule 4. Suspect your register constraints. */
wikitext
text/x-wiki
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Why is inline assembly crashing the compiler or behaving strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile` and input operand
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
=== Rule 4. Suspect your register constraints. ===
[[C Inline Assembly|Register constraints]] must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
d8bc0a164edc61235bc23ebb292dbde5b7c0a39b
561
553
2025-02-28T19:40:01Z
Jbyrd
1
Change ordering of FAQs
wikitext
text/x-wiki
== Why is inline assembly crashing the compiler or behaving strangely? ==
If you've tried to use inline assembly in C and have seen strange behavior, you're in good company. GCC's (and thus Clang's) [[C Inline Assembly|inline assembly]] feature has a lot of rough edges, and it's quite a bit more difficult to use safely than you might think.
The reason for this is, of course, performance. Making the inline assembly feature simpler to reason about would also require the compiler to do less optimization around it. The rules for how it works aren't too terribly complicated, though.
=== Rule 1: Declare your clobbers. ===
Take a look at the following example:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here");
return value;
}
</syntaxhighlight>The calling convention has the value <code>value</code> in the <code>A</code> register on entry, and the return value needs to be in <code>A</code> on exit. The compiler would like to emit this as:<syntaxhighlight lang="asm">
foo:
some compiler-unknown string of inline assembly goes here
rts
</syntaxhighlight>But, is this safe? It depends entirely on the contents of the inline assembly, but that could JSR or BRK, doing who knows what.
There are two assumptions the compiler could make: that <code>A</code> is clobbered, or that <code>A</code> is preserved. Assuming it's preserved is faster, so naturally, this is the assumption that GCC makes. The burden falls on the author to list all clobbered registers except for the flags <code>N</code> and <code>Z</code>. (These aren't explicitly tracked by the compiler, since they're clobbered by so many instructions.)
So, if you write this instead as the following, then the compiler will save and restore A around the inline assembly:<syntaxhighlight lang="c">
char foo(char value) {
asm ("some compiler-unknown string of inline assembly goes here":::"a");
return value;
}
</syntaxhighlight><syntaxhighlight lang="asm">
foo:
sta mos8(__rc2)
some compiler-unknown string of inline assembly goes here
lda mos8(__rc2)
rts
</syntaxhighlight>
=== Rule 2: Inputs, outputs, and <code>volatile</code>. ===
Here's a related example that also might not work:<syntaxhighlight lang="c">
void foo(char value) {
asm ("sta 0x1234"); // bad: missing `volatile` and input operand
}
</syntaxhighlight>The trouble here is that the compiler is under no obligation to keep the variable <code>value</code> in the <code>A</code> register. Actually, the compiler isn't under any obligation to keep the inline assembly around at all. GCC inline assembly statements are considered to consume ''inputs'' to produce ''outputs''; if the outputs are unused (or there aren't any) the compiler is free to delete the whole inline assembly statement as an optimization. I haven't personally observed this, but the compiler does currently tend to move inline assembly statements outside of loops, since they "don't do anything that involves the loop".
In this case, we want to consume the variable <code>value</code> as an input, and produce the effect of storing to 0x1234. This isn't exactly an output, but we can declare that the inline assembly statement has "other" side-effects by declaring it <code>volatile</code> (much like an I/O register is declared volatile).
The correct version of this snippet is:<syntaxhighlight lang="c">
void foo(char value) {
asm volatile ("st%0 0x1234" : /* no output */ : /* input -> */ "R"(value));
}
</syntaxhighlight>The <code>R</code> [[C Inline Assembly|constraint]] on the <code>value</code> input specification says "put this in <code>A</code>, <code>X</code>, or <code>Y</code>". You can insert the register used into the text of the assembly to become <code>sta</code>, <code>stx</code>, or <code>sty</code>, depending on which the compiler ends up picking. It should trivially pick <code>sta</code> in this case, but this gives the compiler flexibility as the code around the inline assembly changes. For an overview of the GNU assembler syntax, including input and output, see [https://www.felixcloutier.com/documents/gcc-asm.html#puts this link].
=== Rule 3: Consider using module-level asm or an assembly file. ===
As you may have gathered from the rather extensive list of concerns above, inline assembly in GCC is really designed for the efficient insertion of assembly snippets to be used as part of the expressions and calculations performed by a larger C algorithm. If you find yourself writing all or nearly all of a routine in assembly, then inline assembly probably isn't the right tool for the job.
Instead, consider writing the entire routine in assembly, and having the routine use the [[C calling convention]]. Then, it can be declared in a C header and called natively from other C routines.
There are broadly two ways to do this: module-level asm or dedicated assembly files.
To use module-level assembly, just place the <code>asm</code> statement at the top level of a file and declare the function as you would in the assembler:<syntaxhighlight lang="c">
asm (
".text\n"
".global foo\n"
"foo:\n"
" sta 0x5678\n"
" rts\n"
);
void foo(char c);
void bar(void) {
foo(c);
}
</syntaxhighlight>Alternatively, you can create a <code>foo.s</code> file, assemble it, and add it to the link. This allows you to call the function as if it was written in C, and keeps a clean interface (i.e, the C calling convention) between C and assembly code.
The downside of these two routes is that you lose the ability for the compiler to inline the function into its callers; fully-assembly functions are opaque in a way that inline assembly isn't. But, that's the tradeoff; you can give the compiler a degree of control over the scheduling and semantics of your assembly semantics, or you can not; the choice is yours.
=== Rule 4. Suspect your register constraints. ===
[[C Inline Assembly|Register constraints]] must be used ''exactly'' as documented. Inadvertently capitalizing them incorrectly, or referring to nonexistent registers, can crash the compiler. Yes, we're aware that compilers should never crash, and we're [https://github.com/llvm-mos/llvm-mos/issues/463 waiting on some upstream changes] to fix this permanently.
== Why is the compiler removing my infinite loops? ==
Here's a really good article on this: [https://stefansf.de/post/non-termination-considered-harmful/ Non-Termination Considered Harmful in C and C++]
The short version is that in C++, infinite loops are undefined behavior, and the compiler is free to assume that they cannot happen. This allows the compiler to emit faster code, so it will typcially do this whenever it can.
In C, the situation is a bit more complicated; it can assume that any nontrivial loops terminate, so long as they don't contain certain things.
Generally, if you want an infinite loop, you have to put something inside it that the C and C++ standard doesn't allow the compiler to elide. The easiest way to do this is typically <code>asm volatile ("");</code>, this tells the compiler that an inline assembly block with uncertain side effects should be inserted, which should keep the loop around. The compiler doesn't do deep introspection into ASM fragments, so it can't prove that it's safe to elide the loop, so it stays.
This requires the inline-assembly extension; to do this in standard C/C++, just insert a volatile load or store into the loop. This will similarly force the compiler to keep the loop.
The reason for all this is that it allows compilers to assume that code underneath loops will eventually be reached, which allows a variety of optimizations to occur. Modern C/C++ compilers like Clang operate by tearing down the code as written and rewriting it, bit by bit, into something more efficient. The only real constraints on this process are the letter of the C standard; undefined behavior exists to permit generating better code; it just creates unfortunate foot-guns like this one.
== Why is the compiler removing my memory accesses? ==
This is very similar to the above; if a memory access absolutely has to happen at a particular point in the program, it has to be done through a volatile object. Otherwise, the compiler is free to reason its heart out about what purpose that memory access serves, and if one isn't visible to the compiler, the access may be removed. This is usually very desirable; spurious accesses crop up all the time. For example, if you're incrementing a 16-bit counter variable, and some particular path through the program only ever looks at the low byte, the compiler might skip the high part of the increment. But, you wouldn't want this to happen if this variable was referenced by an OS routine! So, volatile is the blessed way to tell the compiler: "this needs to happen now!".
== Why isn't the linker placing my sections? ==
There are a two main reasons this can occur; the issue could be due to either or both.
First, because the section wasn't marked "allocatable", which unfortunately isn't the default on linkers compatible with GNU ld for sections without a standard name (e.g., <code>.data</code>). Allocatable just means that the section should take up space in the final binary, as opposed to things that are merely there for linker or CLI utility use, like symbol tables. The syntax to add this flag is: <code>.section ''name'',"a"</code>
Second, because no symbols defined in the section were referenced, the linker may garbage-collect away the section. The linker has a sense of which sections must intrinsically be present (the logic is a bit complex, but it usually does a good job). Any sections that can't be reached from those known roots are removed, to save space. This is one of the features that allows uncalled functions to be stripped out of the binary at link time. To solve this, you can add the "retain" flag to the section declaration: <code>.section ''name'',"R"</code>. Another way to do this is to add <code>KEEP</code> to the linker script when assigning the section: <code>''output_section { KEEP(input_section) }''</code>
See the [https://sourceware.org/binutils/docs/as/Section.html GNU assembler manual] for the section directive and [https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep GNU ld manual] for KEEP.
== Does llvm-mos support 24-bit integers? ==
Unofficially, yes, via the C extension <code>_BitInt(n)</code>. You can say <code>_BitInt(24) x;</code> or <code>unsigned _BitInt(24) x;</code> and the results will behave more or less how one would expect.
We can't officially support an <code>int24_t</code> without also including a special <code>printf</code> and <code>scanf</code> syntax in the SDK, since the standard mandates that e.g. the macro <code>PRId24</code> would resolve to this syntax. This is something we can do, but the priority is relatively low. It's tracked as an [https://github.com/llvm-mos/llvm-mos-sdk/issues/301 issue] against the SDK.
[[Category:Main]]
7b2b8136f8439f963776de07a704f5f14d505718
Imaginary registers
0
42
518
477
2024-02-25T15:36:35Z
162.158.103.102
0
rewrite and clarify
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of memory locations reserved by the linker.
(The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.)
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume that imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
1b6c36edc8a0a69c7ddbdb61578712276ed34a8a
532
518
2024-03-12T18:38:21Z
Jbyrd
1
Clarification on consecutive zero page regions for imaginary registers
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of memory locations reserved by the linker.
(The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.)
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume the __rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each __rs* register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
0cba0f14a0f3b602e5dc307ec39b4c6e04dd6045
533
532
2024-03-12T18:38:45Z
Jbyrd
1
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of memory locations reserved by the linker.
(The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.)
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume the __rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each __rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
12a2c8a20f7f18d4c0d2903e9664b599e8f01408
534
533
2024-03-12T18:40:36Z
Jbyrd
1
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker.
(The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.)
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume the __rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each __rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
2936058a6fbab40b0fdd3d1751a3000d6e65c808
535
534
2024-03-12T18:42:13Z
Jbyrd
1
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker.
(The term ''imaginary registers'' is used because ''virtual registers'' have another pre-defined meaning in LLVM compiler development.)
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume the rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
f16b440c76b8ac2d3c730efc706d991cdaf370e5
558
535
2024-11-11T10:10:37Z
Jbyrd
1
Add link to virtual register definition
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker.
(The term ''imaginary registers'' is used because ''virtual registers'' have [https://llvm.org/docs/CodeGenerator.html#register-allocator another pre-defined meaning] in LLVM compiler development.)
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume the rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
a35f2f2c7b7f50338ed633a8d4125cdb420029a8
559
558
2024-11-11T10:14:08Z
Jbyrd
1
More detail on register types
wikitext
text/x-wiki
Like all modern compilers, LLVM assumes generally that the target machine has a large number of target registers that are more or less interchangeable. This restriction is loosened, at some cost to code complexity, for the X86 targets, but generally most targets assume that you have a range of compiler-controllable registers. However, the 6502 and its derivatives have only three registers with very different available functionality - A, X and Y. How does one make the two work together?
The 6502 CPU architecture also features a special range of memory referred to as the ''zero page'' - this typically refers to the first 256 bytes of memory. This area of memory is faster to access and modify; it also provides some additional addressing modes not available in general memory accesses. Due to the increased performance and capability of this area, it is idiomatic for 6502 code to use some of it as temporary variable space. This, then, allows reconciling LLVM's needs with the 6502's features - by defining an area of memory as ''imaginary registers''; that is, registers which do not exist in silicon, but are provided by a set of zero-page memory locations reserved by the linker. We use the term ''imaginary registers,'' because ''virtual registers'' have [https://llvm.org/docs/CodeGenerator.html#register-allocator another pre-defined meaning] in LLVM compiler development.
These imaginary registers are represented at code generation time by a symbol like __rc17 or __rs5. When linking, this symbol is translated to an actual memory address by the linker. Each ?c register contains a char type, which is 8 bits; each ?s register contains a short type, which is 16 bits.
Presently, the compiler requires 16 imaginary pointers, each consisting of two contiguous bytes. These are called rs0 - rs15. Each such pointer is divided into two subregisters; e.g. the low byte of rs0 is rc0, and the high byte is rc1. Continuing on in this fashion defines rc0 - rc31.
Note that LLVM-MOS does not assume the rs* imaginary registers need to be consecutive! Many targets have non-consecutive usable zero page memory locations. However, LLVM-MOS does assume that each rs* imaginary register consists of two neighboring bytes in memory. It is possible to split the imaginary register ranges into subsequences containing even numbers of bytes, if a particular target requires it.
[[Category:Code generation]]
[[Category:Assembly]]
[[Category:C]]
e8f7a8907cfebf3f98488c390ea4752be29665d6
Modifiers
0
16
529
483
2024-03-12T16:37:05Z
162.158.102.187
0
Add mos24()
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Description
|-
| <code>mos8()</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16lo()</code> or <code><</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> or <code>></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24()</code> || Forces a symbol to be considered a 24-bit (long) address.
|-
| <code>mos24bank()</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || The highest byte in the segment of the given 24-bit address.
|}
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
Much existing MOS code depends on the less-than operator <code><</code> and the greater-than operator <code>></code> as shorthand for the mos16lo() and mos16hi() functions, respectively. This functionality has been added to MOSAsmParser.cpp, so that you can use either representation in your own programs.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar).
[[Category:Assembly]]
731a6817248d621c123d4da4ac4c3bc7c41c301f
541
529
2024-07-28T07:29:17Z
Asie
12
update for v15.0.0 changes
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator.
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Short modifier !! Description
|-
| <code>mos8()</code> || <code><</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16()</code> || <code>!</code> || Forces a symbol to be considered a 16-bit (absolute) address.
|-
| <code>mos16lo()</code> || <code>#<</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> || <code>#></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24()</code> || <code>></code> || Forces a symbol to be considered a 24-bit (long) address.
|-
| <code>mos24bank()</code> || <code>#^</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || || The highest byte in the segment of the given 24-bit address.
|}
The short modifiers follow the WDC standard as set out in the 65C816 datasheet.
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar).
[[Category:Assembly]]
c6914f1b0aee299ea04b88b3cad770596526354b
542
541
2024-07-28T07:29:51Z
Asie
12
wikitext
text/x-wiki
When writing MOS assembly code, it's often necessary to refer to a specific byte or short within a larger address, when the exact address is not known until code relaxation or linking. For example, code may need to refer to the low byte of a 16-bit function address:
LDA ''[the lowest byte in the 16-bit address of QSORT]''
However, the address of <code>QSORT</code> may not be exactly known, until your program has had relaxation and/or linking applied to it. Therefore, the lowest byte of <code>QSORT</code> won't be known until then as well.
In LLVM land, that operator is referred to as a '''modifier'''. The llvm-mos project supports some MOS-specific modifiers, to refer to a specific part of a larger address. If <code>QSORT</code> refers to a 16-bit address:
LDA #mos16lo(QSORT)
then the lowest byte in the <code>QSORT</code> address is loaded into the accumulator. Alternatively, a shorthand can be used in the form of:
LDA #<QSORT
The following modifiers are available for your use:
{| class="wikitable sortable"
|+ MOS Modifiers
|-
! Modifier !! Short modifier !! Description
|-
| <code>mos8()</code> || <code><</code> || Forces a symbol to be considered an 8-bit (zero page) address.
|-
| <code>mos16()</code> || <code>!</code> || Forces a symbol to be considered a 16-bit (absolute) address.
|-
| <code>mos16lo()</code> || <code>#<</code> || The lowest byte in the given 16-bit address.
|-
| <code>mos16hi()</code> || <code>#></code> || The highest byte in the given 16-bit address.
|-
| <code>mos24()</code> || <code>></code> || Forces a symbol to be considered a 24-bit (long) address.
|-
| <code>mos24bank()</code> || <code>#^</code> || The 8-bit bank portion of the given 24-bit address.
|-
| <code>mos24segment()</code> || || The segment 16-bit portion of the given 24-bit address.
|-
| <code>mos24segmentlo()</code> || || The lowest byte in the segment of the given 24-bit address.
|-
| <code>mos24segmenthi()</code> || || The highest byte in the segment of the given 24-bit address.
|}
The short modifiers follow the WDC standard as set out in the 65C816 datasheet.
The assembly parser tries to be smart about what kinds of modifiers can be used for what purposes. For example, <code>mos24segment()</code> returns a 16-bit address, so you can't do <code>LDA #mos24segment(QSORT)</code> because the immediate LDA instruction only accepts 8-bit values.
All MOS processors are little endian, so the lowest byte appears at the zeroth position in a 16-bit address.
The above syntax for modifiers only works inside assembly language instructions. If you need to use modifiers in assembly directives, the syntax is instead: <code>.byte address@mos16hi</code> (or similar).
[[Category:Assembly]]
11e6203e1f9a0282d99ead7426cc878ecac34867
ELF specification
0
15
530
482
2024-03-12T16:48:29Z
Asie
12
0.3.1: Define mapping symbols.
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.1 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
=== Mapping symbols ===
A given ELF section may contain instructions whose decoding or functional meaning is affected by CPU state. To allow utilities to process such sections appropriately, these sequences of instructions should be marked using mapping symbols.
{| class="wikitable"
|+
!Name
!Type
!Description
|-
|$ml
|STT_NOTYPE
|65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0.
|-
|$mh
|STT_NOTYPE
|65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1.
|-
|$xl
|STT_NOTYPE
|65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0.
|-
|$xh
|STT_NOTYPE
|65816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
3fb631cb60b25d5c9627988fb93a5db214bb68df
531
530
2024-03-12T16:49:12Z
Asie
12
/* Mapping symbols */
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.1 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
=== Mapping symbols ===
A given ELF section may contain instructions whose decoding or functional meaning is affected by CPU state. To allow utilities to process such sections appropriately, these sequences of instructions should be marked using mapping symbols.
{| class="wikitable"
|+
!Prefix
!Type
!Description
|-
|$ml
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0.
|-
|$mh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1.
|-
|$xl
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0.
|-
|$xh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
f7852485334c43f1cde862040f4d4c909c1efdd1
536
531
2024-03-12T19:29:32Z
Asie
12
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.1 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
=== Mapping symbols ===
Mapping symbols are regular ELF symbols whose names are prefixed with a string of characters that begins with the symbol <code>$</code>. They mark the start of a sequence of instructions which are to be interpreted under the assumption of a particular processor mode or function, as denoted by the mapping symbol itself. This functionality is intended to allow utilities working with ELF files to appropriately decode and interpret sequences of instructions whose format or function is affected by CPU state. While various utilities may use mapping symbols in this way, they do not affect runtime behavior and serve only as hints.
The complete list of mapping symbols defined by the specification is listed below:
{| class="wikitable"
|+
!Name prefix
!Type
!Description
|-
|$ml
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0.
|-
|$mh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1.
|-
|$xl
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0.
|-
|$xh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
8e90c1741eb4c747d555c0f29a0d9377b41679ea
537
536
2024-06-20T23:10:09Z
Jbyrd
1
Change description of R_MOS_PCREL_16
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.1 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65el02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|A 16-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
=== Mapping symbols ===
Mapping symbols are regular ELF symbols whose names are prefixed with a string of characters that begins with the symbol <code>$</code>. They mark the start of a sequence of instructions which are to be interpreted under the assumption of a particular processor mode or function, as denoted by the mapping symbol itself. This functionality is intended to allow utilities working with ELF files to appropriately decode and interpret sequences of instructions whose format or function is affected by CPU state. While various utilities may use mapping symbols in this way, they do not affect runtime behavior and serve only as hints.
The complete list of mapping symbols defined by the specification is listed below:
{| class="wikitable"
|+
!Name prefix
!Type
!Description
|-
|$ml
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0.
|-
|$mh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1.
|-
|$xl
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0.
|-
|$xh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
c35876799637623fe1d3dc4c0e3db467e00e1a20
538
537
2024-06-28T19:48:22Z
Jbyrd
1
Fixed 65ce02 link
wikitext
text/x-wiki
== ELF specification for MOS-compatible processors ==
This is version 0.3.1 of this specification.
Comments and improvements are solicited at johnwbyrd at gmail dot com.
=== Overview ===
This is a specification to extend the Executable and Linking Format (ELF) to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
* [https://semver.org/ Semantic Versioning 2.0.0]
=== ELF header ===
The EI_CLASS field of the e_ident[] identification index, as defined in the ELF header, should be set to ELFCLASS32, which has a value of 1. This identifies ELF files as having (at most) 32 bits of address space.
Although the ELF format encompasses 32-bit address types, it does not require them. MOS processors can access 16 bits by default and 24 bits at most. When an 8-bit, 16-bit, or 24-bit address is stored in a 32-bit ELF address type, the unused high bits should be zeroed.
Unused bits in any 32-bit ELF address field should be set to zero. If these unused bits are set in any ELF file, then tool behavior is undefined. However, if a tool detects that these unused bits are set, then this condition may be flagged as a warning or as an error by the tool.
=== Machine type field ===
The e_machine field shall have a constant name of EM_MOS. The constant of EM_MOS shall be set to 6502 (decimal; equivalent to 0x1966 in hexadecimal).
The e_flags field bits shall have the following meanings. These values may be logically OR'ed to indicate that multiple features are present.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| EM_MOS_6502 || 0x00000001 || MOS 6502 "generic" instructions, also known as NMOS 6502, as defined in the [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Microcomputer Family Programming Manual, Second Edition]. This bit should be set, unless the target architecture is incapable of running 6502 family instructions, such as would be the case in a virtual machine such as SWEET16 or bbcvm.
|-
| EM_MOS_6502_BCD || 0x00000002 || MOS 6502 compatible [http://www.6502.org/tutorials/decimal_mode.html BCD (binary coded decimal) instruction handling], including CLD and SED instructions for the 6502 series. For most 6502 compatible cores, this bit should be set to one, to indicate that BCD mode support is present on the target processor. However, some 6502 near-compatible cores, such as the [https://en.wikipedia.org/wiki/Ricoh_2A03 Ricoh 2A0x], have their BCD mode disabled; this bit should be set to zero if the target core is not expected to support BCD. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_6502X || 0x00000004 || NMOS 6502 "illegal" opcodes including ALR, ANC, ARR, AXS, DCP, ISC, LAS, LAX, RLA, RRA, SAX, SLO, and SRE, as defined in the Illegal Opcodes section of the [http://www.oxyron.de/html/opcodes02.html 6502/6510/8500/8502 Opcode matrix]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_65C02 || 0x00000008 || MOS 65C02 generic additional instructions and addressing modes, including BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
| EM_MOS_R65C02 || 0x00000010 || MOS 65C02 Rockwell/WDC additional instructions, including BBR, BBS, RMB, and SMB, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65C02 || 0x00000020 || MOS 65C02 WDC only instructions, including STP and WAI, as defined in [http://6502.org/tutorials/65c02opcodes.html 65C02 Opcodes]. This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_W65816 || 0x00000100 || MOS 65C816 native mode instructions and addressing modes, as defined in [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816, Including the 6502, 65C02 and 65802] and [http://6502.org/tutorials/65c816opcodes.html 65C816 Opcodes]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_W65C02 bits are set to one.
|-
| EM_MOS_65EL02 || 0x00000200 || 65EL02 only instructions and addressing modes, as defined in [http://www.eloraam.com/nonwp/redcpu.php the RedPower CPU Instruction Table] This bit may be set to one only if the EM_MOS_6502 and EM_MOS_65C02 bits are set to one.
|-
| EM_MOS_65CE02 || 0x00000400 || 65CE02 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-65ce02 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_HUC6280
|0x00000800
|HuC6280 only instructions, as defined in [https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html Chris Covell's opcode matrix reference]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, and EM_MOS_R65C02 bits are set to one.
|-
|EM_MOS_65DTV02
|0x00001000
|Instructions specific to the C64DTV implementation of the 6502, as defined in [http://tass64.sourceforge.net/#opcodes-65dtv02 64tass reference manual] . This bit may be set to one only if the EM_MOS_6502 bit is set to one.
|-
|EM_MOS_4510
|0x00002000
|CSG 4510 only instructions, as defined in [http://tass64.sourceforge.net/#opcodes-4510 64tass reference manual]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02 and EM_MOS_65CE02 bits are set to one.
|-
|EM_MOS_45GS02
|0x00004000
|45GS02 only instructions, as defined in [https://github.com/MEGA65/mega65-user-guide/blob/master/appendix-45gs02-registers.tex the MEGA65 User Guide]. This bit may be set to one only if the EM_MOS_6502, EM_MOS_65C02, EM_MOS_R65C02, EM_MOS_65CE02 and EM_MOS_4510 bits are set to one.
|-
|EM_MOS_SPC700
|0x00020000
|Instructions used on the Sony SPC700 architecture. As the architecture is similar to, but not binary-compatible with, the 6502, this bit may be set to one only if no other bits are set to one.
|}
Behavior of a binary resulting from linking incompatible combinations of e_flags values, is undefined. Linkers are suggested, but not required, to emit warnings when a flag mismatch exists between ELF objects, libraries, and/or executables.
=== Section flags ===
Each ELF section header contains a 32-bit word named sh_flags. For MOS-format ELF files, these flags have special meanings.
{| class="wikitable sortable"
|+ e_flags bit fields
|-
! Name !! Value !! Description
|-
| SHF_MOS_ZEROPAGE || 0x10000000 || This bit should be set to one if this ELF section is to be placed in zero page or direct page. An assembler or linker may use this information to determine whether addresses in this section will be 8 bits long.
|}
=== Relocation types ===
Relocations should behave as specified in the Relocations section of the [https://refspecs.linuxfoundation.org/elf/elf.pdf ELF specification] as described earlier. Each relocation entry in a MOS ELF file describes a particular MOS relocation. The lowest byte in the r_info field in an Elf32_Rel or Elf32_Rela relocation entry, is treated as a MOS-specific relocation type. (The ELF specification refers to this as the ELF32_R_TYPE macro.)
The exact types of MOS-specific relocations for ELF can be viewed in [https://github.com/llvm-mos/llvm-mos/blob/main/llvm/include/llvm/BinaryFormat/ELFRelocs/MOS.def MOS.def] as part of the llvm-mos distribution.
{| class="wikitable"
|+
!Name
!Value
!Description
|-
|R_MOS_NONE
|0
|No relocation type. Should not be emitted by the writer. Readers should recognize this as an error in the ELF file.
|-
|R_MOS_IMM8
|1
|An 8-bit immediate value.
|-
|R_MOS_ADDR8
|2
|An 8-bit (e.g. zero page or direct page) address.
|-
|R_MOS_ADDR16
|3
|A 16-bit address. Standard for most MOS processors.
|-
|R_MOS_ADDR16_LO
|4
|The least significant byte of a 16-bit address. All MOS processors are little endian, so this would also be the leftmost byte in a 16-bit address.
|-
|R_MOS_ADDR16_HI
|5
|The most significant byte of a 16-bit address.
|-
|R_MOS_PCREL_8
|6
|An 8-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-126, +129]. Used for the B?? series of MOS instructions.
|-
|R_MOS_ADDR24
|7
|A 24-bit address, used in the 65816 series.
|-
|R_MOS_ADDR24_BANK
|8
|The 8-bit bank value from a 24-bit address. The most significant byte.
|-
|R_MOS_ADDR24_SEGMENT
|9
|The 16-bit segment value from a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_LO
|10
|The low byte from the segment value in a 24-bit address.
|-
|R_MOS_ADDR24_SEGMENT_HI
|11
|The high byte from the segment value in a 24-bit address.
|-
|R_MOS_PCREL_16
|12
|A 16-bit PC-relative jump value, calculated from the end of the instruction. From the beginning of the instruction, the jump range is [-32765, +32770], however, this jump range is wrapped at bank boundaries. Used for the BRL instruction on the 65C816.
|-
|R_MOS_FK_DATA_4
|13
|A generic four-byte, 32-bit value. Although MOS does not use native 32-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_FK_DATA_8
|14
|A generic eight-byte, 64-bit value. Although MOS does not use native 64-bit relocations, DWARF may use this relocation type when describing debug information.
|-
|R_MOS_ADDR_ASCIZ
|15
|A null-terminated, decimal ASCII string of the value. Generated by .mos_addr_asciz assember directive. See [[Assembler]] for details.
|-
|R_MOS_IMM16
|16
|A 16-bit immediate value.
|-
|R_MOS_ADDR13
|17
|A 13-bit address, stored in the low 13 bits of a 16-bit value. Used only on the SPC700.
|-
|}
=== Mapping symbols ===
Mapping symbols are regular ELF symbols whose names are prefixed with a string of characters that begins with the symbol <code>$</code>. They mark the start of a sequence of instructions which are to be interpreted under the assumption of a particular processor mode or function, as denoted by the mapping symbol itself. This functionality is intended to allow utilities working with ELF files to appropriately decode and interpret sequences of instructions whose format or function is affected by CPU state. While various utilities may use mapping symbols in this way, they do not affect runtime behavior and serve only as hints.
The complete list of mapping symbols defined by the specification is listed below:
{| class="wikitable"
|+
!Name prefix
!Type
!Description
|-
|$ml
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 0.
|-
|$mh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the M processor flag is set to 1.
|-
|$xl
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 0.
|-
|$xh
|STT_NOTYPE
|65C816, 65EL02: Indicates the start of a sequence of instructions for which it should be assumed that the X processor flag is set to 1.
|-
|}
== References ==
[https://www.commodore.ca/manuals/funet/cbm/documents/chipdata/65ce02.txt Commodore Semiconductor Group CSG65CE02 Technical Reference]
[http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MCS6500 Family Microcomputer Programming Manual, January 1976]
[https://refspecs.linuxfoundation.org/elf/elf.pdf Tool Interface Standard (TIS), Executable and Linking Format (ELF) Specification, Version 1.2]
[http://bigfootinformatika.hu/65el02/archive/65el02_instructions.txt 65EL02 Instruction Set Extension Reference]
[http://shu.emuunlim.com/download/pcedocs/pce_cpu.html PC-Engine Documentation: The HuC6280 CPU]
[https://snes.nesdev.org/wiki/SPC-700_instruction_set SPC-700 instruction set - SNESdev Wiki]
[[Category:Linking]]
790d84143ff63e534a76853b9697df9bccb59212
Current status
0
25
539
308
2024-07-10T17:00:25Z
MarkTheStrange
21
Clarify relative branch range.
wikitext
text/x-wiki
== C compiler ==
* Code generation for the Commodore 64, Atari 800, and an included 6502 simulator.
* The high and low-level optimizations expected of a young-ish LLVM backend
** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers
** The imaginary registers can be placed anywhere and need not be contiguous.
** The calling convention passes through registers whenever possible.
** Loop optimizations to select 6502 addressing modes
** Whole program "static stack" optimization
*** Automatically identifies non-reentrant functions and allocates their frames as static globals
*** Programs without recursion or complex function pointers may not need a soft stack at all.
*** No manual annotations required
** Link time inlining and optimization across the whole program
*** Includes SDK libraries. Library calls can be often optimized away completely!
* Broad C99 and C++11 freestanding standards compatibility
** Interrupt handling
** C++ templates
** C++ virtual functions
** C++ new/delete
** C++ Run-Time Type Information (dynamic_cast, typeid)
** C++ static constructors/destructors (run before and after main)
** C++ "magic" function local static constructors/destructors
* Excellent compiler usability
** Clang's world-class error messages
** IDE integration through the included custom clangd's Language Server Protocol
** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code>
* A small standard library sufficient to provide the above and a few extras
** Simple printf
** Simple malloc/free
** exit, _Exit, and atexit
=== Notable Exceptions ===
* C99 requires supporting 64KiB locals
* Hosted C with all the standard library bells and whistles.
* Float/double
* C++ Exceptions
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, where the usual signed-byte range of [-128,+127] applies to the address of the instruction _following_ the branch (the one executed next if the branch is skipped) rather than the branch instruction itself. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
786691b5b749d255316393b481faad8c52da57d7
540
539
2024-07-10T17:01:07Z
MarkTheStrange
21
/* Assembler */ formatting
wikitext
text/x-wiki
== C compiler ==
* Code generation for the Commodore 64, Atari 800, and an included 6502 simulator.
* The high and low-level optimizations expected of a young-ish LLVM backend
** Sophisticated register allocation over A, X, Y, and a field of 16 2-byte zero-page (imaginary) registers
** The imaginary registers can be placed anywhere and need not be contiguous.
** The calling convention passes through registers whenever possible.
** Loop optimizations to select 6502 addressing modes
** Whole program "static stack" optimization
*** Automatically identifies non-reentrant functions and allocates their frames as static globals
*** Programs without recursion or complex function pointers may not need a soft stack at all.
*** No manual annotations required
** Link time inlining and optimization across the whole program
*** Includes SDK libraries. Library calls can be often optimized away completely!
* Broad C99 and C++11 freestanding standards compatibility
** Interrupt handling
** C++ templates
** C++ virtual functions
** C++ new/delete
** C++ Run-Time Type Information (dynamic_cast, typeid)
** C++ static constructors/destructors (run before and after main)
** C++ "magic" function local static constructors/destructors
* Excellent compiler usability
** Clang's world-class error messages
** IDE integration through the included custom clangd's Language Server Protocol
** Straigtforward invocations to compile for various targets: <code>mos-c64-clang++ -Os -o game.prg game.cc</code>
* A small standard library sufficient to provide the above and a few extras
** Simple printf
** Simple malloc/free
** exit, _Exit, and atexit
=== Notable Exceptions ===
* C99 requires supporting 64KiB locals
* Hosted C with all the standard library bells and whistles.
* Float/double
* C++ Exceptions
== Assembler ==
The [[assembler]], llvm-mc, understands and assembles all NMOS 6502 opcodes. The assembler correctly understands symbols, and it's possible to use them as branch targets, do pointer math on them, and the like. Fixups work as expected at link time.
The assembler correctly deals with 6502 relative branches. BEQ, BCC, etc., all correctly calculate PC relative offsets in the unusual 6502 convention, where the usual signed-byte range of [-128,+127] applies to the address of the instruction ''following'' the branch (the one executed next if the branch is skipped) rather than the branch instruction itself. Since llvm-mc is GNU assembler compatible, you can use all GNU assembler features while writing 65xx code, including macros, ifdefs, and similar.
The assembler is capable of intelligently figuring out whether symbols should refer to zero page or 16-bit locations, at the time of compilation. If, at compile time, you place a symbol in a section named <code>.zeropage</code>, <code>.directpage</code>, or <code>.zp</code>, then that symbol will be assumed to be located in zero page; otherwise, it will be assumed to refer to a 16-bit address. Additionally, if a symbol is placed in a section with a <code>z</code> section flag enabled, then that symbol is assumed to be located in zero page, with addressing calculated accordingly.
The assembler and linker both understand that $ is a legal prefix for hexadecimal constants. Much existing 6502 assembly code depends on this older convention. See the DollarIsHexPrefix constant in MCAsmInfo.h. The lexer now queries whatever the current MCAsmInfo structure to see whether the target wants the dollar sign to be a hex prefix. So, everything that depends on the lexer (which is almost everything in LLVM) can now recognize 6502 format hexadecimal constants, if the corresponding MCAsmInfo asks for it. The modern 0x prefix works fine as well.
The assembler understands the names of the 6502 registers, including a, x, y, p, sp, and pc. It understands references to these names to be references to those registers. If your code depends on these names as variable or section names, you can force the assembler to use the prefix of llvm_mos_ on those registers, e.g. llvm_mos_a, llvm_mos_x, etc. To require this prefix on references to those registers, enable the <code>mos-long-register-names</code> compilation feature. For example, with llvm-mc, use the flag <code>-mattr=+mos-long-register-names</code>. Printed assembly output uses this naming scheme to avoid conflicts with existing code.
To target MOS family processors, you will need to use a triple of "mos" (try: <code>-triple mos</code>) as a parameter to any tool.
== Linker ==
The ELF file format for objects and executables, has been extended to support 65xx compatible processors. Hello-world type programs have been proven to compile, and work as expected, on emulated Commodore 64, VIC-20, and Apple II machines.
The [[linker]], lld, can be called with a "-flavor gnu" parameter in order to permit linking of ELF executables.
If you've written an appropriate linker file for your 6502 target, you can season the following overly verbose formula to compile assembly code for your particular target, where %s is the name of your assembly source, %S is the directory of your include files, and %t is the base name of your project:
<code>llvm-mc -triple mos --filetype=obj -o=%t.obj %s
llvm-objdump --all-headers --print-imm-hex -D %t.obj
llvm-readelf --all %t.obj
lld -flavor gnu %t.obj -o %t.elf -L %S your-target.ld
llvm-readelf --all %t.elf
llvm-objdump --all-headers --print-imm-hex -D %t.elf
llvm-objcopy --output-target binary --strip-unneeded %t.elf %t.bin</code>
The llvm-objdump and llvm-readelf programs are not necessary; they're just there to help you debug your own pipeline.
As of this writing, some example linker files and BASIC stubs exist at llvm/test/MC/MOS/Inputs.
== ELF ==
Both the assembler and the linker support the [[ELF format]], for both object files and executables. The ELF format has been extended with a machine type of 6502 (naturally) to permit storing 65xx code in ELF files.
Because the 6502 assembler and linker both work with ELF files, you can use any of your favorite tools to inspect or understand ELF files generated by the LLVM tools. The llvm-readobj, llvm-objdump, llvm-objcopy, llvm-strip, and likely the other command line tools as well, work as expected. This also means that generic tools that work on ELF files, such as this online ELF viewer, can read and dump basic information about MOS executables.
== Deployment ==
If any branch builds and passes the <code>check-all</code> suite of LLVM tests, it's immediately deployed so you can get fresh bits for your favorite platform. Always grab the main build first. We smoke test on Windows, MacOS, and Ubuntu, but llvm-mos should build on anything that LLVM can build on. You can download fresh binary bits from the [https://github.com/llvm-mos/llvm-mos/releases Github releases page].
== Implementation ==
TableGen has been taught all native 6502 instructions and formats. llvm-mc can assemble all 6502 instructions..
For some examples of what the backend can do as of this writing, see the llvm/test/MC/MOS directory for some functional assembler tests. Building the check-llvm-mc-mos target, confirms just these tests for MOS.
[[Category:Main]]
05879598a91e4af94069b6ba7a4c97b81d035f7c
C compiler
0
18
543
467
2024-08-01T13:25:16Z
Wombat
9
Remove outdated statement about missing floating support.
wikitext
text/x-wiki
A backend has been added to Clang to support the MOS instruction set. This backend may be targeted by adding the flag <code>--target=mos</code> to clang.
Different CPUs may be selected using the <code>-mcpu</code> flag, for example <code>-mcpu=mosw65816</code>.
== Defines ==
The compiler defines the <code>__mos__</code> macro for the <code>mos</code> target. In addition, defines are added for every CPU which is compatible with the code; for instance <code>-mcpu=mos65c02</code> additionally defines <code>__mos6502__</code> and <code>__mos65c02__</code>.
== Known issues ==
The Clang is broadly compatible with the freestanding portion of the C99 standard. Some of the GCC and Clang language extensions work, some don't. Similarly some of the C++ language features work, some don't. We haven't exhaustively catalogued this; there's a lot, and some are extremely obscure and nearly useless.
Still, some compiler extensions are de-facto standard, so we'll call out a few caveats for those:
* The GCC/Clang alignment directives work only for global and static variables, not automatic (local) variables. Aligning the stack pointer is tricky, since it requires use of a frame pointer; but the rigors of 6502 indexing cause our frame pointer to be useless for this purpose. To get this to work, we may need *two* frame pointers. Expected call for this feature is low, so if you need it, let us know, and we'll reprioritize.
[[Category:C]]
dd4a6986cebe1be96844cbb9cbced1f16c0e13cd
Zig
0
72
548
2024-09-23T12:33:27Z
Kassane
22
zig-mos page
wikitext
text/x-wiki
The [https://github.com/kassane/zig-mos-bootstrap zig-mos project] is based on llvm-mos and llvm-mos-sdk and allows Zig programs to run on 6502. If you want to use zig-mos, just get the available tarball (compatible with your operating system and computer architecture). If it's not available for your system, downloading bootstrap and running the build.(sh|bat) script on your local system to start building (with all dependencies already available) - no additional dependencies required!
=== Getting started ===
Once you've downloaded or built zig-mos, you can move on to the next step. It's also recommended to download [https://github.com/llvm-mos/llvm-mos-sdk llvm-mos-sdk], since the libraries and additional files aren't included in zig-mos.
Let's test zig-mos by downloading the [https://github.com/kassane/zig-mos-examples zig-mos-examples] project.
Having downloaded and extracted all the contents of the [https://github.com/kassane/zig-mos-examples zig-mos-examples] project, you can then select which project to build and later test using a specified emulator or device.
=== Commands ===
==== Zig command line interface ====
* <code>build-lib</code>: build static-lib or shared-lib (add <code>-dynamic</code> flag);
* <code>build-obj</code>: build object file, like <code>clang/gcc -c</code>.
* <code>build-exe</code>: build executable
* <code>build</code>: build-system mode, need <code>build.zig</code>.
==== Clang command line interface ====
* <code>zig cc</code>: clang CLI
* <code>zig c++</code>: clang++ CLI (uses <code>llvm-libc++</code> + <code>llvm-libunwind</code> by default)
'''Note:''' Zig toolchain does not change `libclang` codegen. However, the default config enable <code>-fsanitize=undefined</code>.
==== Targets available ====
<syntaxhighlight lang="bash">
$> zig build-obj --show-builtin -target mos-freestanding -mcpu=
info: available CPUs for architecture 'mos':
mos4510
mos45gs02
mos6502
mos6502x
mos65c02
mos65ce02
mos65dtv02
mos65el02
moshuc6280
mosr65c02
mosspc700
mossweet16
mosw65816
mosw65c02
error: unknown CPU: ''
</syntaxhighlight>
'''Note:''' Freestanding targets are not listed on <code>zig targets | jq .libc</code> (triple-targets)
==== CPU Features ====
Similar to [Targets available](#targets-available) command, add <code>-mcpu</code> or <code>-Dcpu=</code> (need <code>build.zig</code>).
* <code>+</code> add feature
* <code>-</code> remove feature
'''Note:''' For show feature list add <code>+</code>/<code>-</code> without feature name
'''e.g.:'''
<syntaxhighlight lang="bash">
$> zig build-obj --show-builtin -target mos-freestanding -mcpu=mos6502+
info: available CPU features for architecture 'mos':
mos4510: CSG 4510
mos45gs02: 45GS02
mos6502: Generic MOS 6502, with support for BCD instructions
mos6502x: NMOS 6502, with illegal opcode support
mos65c02: Generic MOS 65C02
mos65ce02: Commodore 65CE02
mos65dtv02: The C64DTVs 6502 variant
mos65el02: 65EL02 virtual machine
mos_insns_4510: The new instructions present on CSG 4510
mos_insns_45gs02: The new instructions present on 45GS02
mos_insns_6502: The original documented 6502 instruction set
mos_insns_6502bcd: BCD instruction support, including SED and CLD (most 6502 series CPUs support this)
mos_insns_6502x: The 'illegal' opcodes present on some early variants of the original 6502 processor
mos_insns_65c02: The new and modified instructions present on the generic 65c02 and variants
mos_insns_65ce02: The new and modified instructions present on 65ce02 and variants
mos_insns_65dtv02: The new and modified instructions present on the C64DTVs 6502 variant
mos_insns_65el02: The new and modified instructions present on 65EL02
mos_insns_huc6280: The new and modified instructions present on HuC6280
mos_insns_r65c02: The new and modified instructions present on Rockwell and WDC 65c02
mos_insns_spc700: The SPC700 instruction set
mos_insns_sweet16: The SWEET16 instruction set
mos_insns_w65816: The new and modified instructions present on WDC 65816
mos_insns_w65c02: The new and modified instructions present on WDC 65c02
mos_long_register_names: Requires llvm_mos_* prefixes to all registers. Useful if your code has variable names that conflict with llvm-mos register names
moshuc6280: Hudson Soft HuC6280
mosr65c02: Rockwell 65C02
mosspc700: Sony 6502-like CPUs, including the SPC700
mossweet16: MOS 6502 compatible with SWEET16 virtual machine support
mosw65816: WDC 65816
mosw65c02: WDC 65C02
static_stack: Whether to use statically-allocated stack frames if possible.
error: unknown CPU feature: ''
</syntaxhighlight>
==== Target info (builtin) ====
'''Note:''' If like syntax-highlighting use <code>| bat -p -l zig</code> pipeline command or save this output as <code>builtin.zig</code> and open on your code editor.
<syntaxhighlight lang="bash">
$> zig build-obj --show-builtin -target mos-freestanding
</syntaxhighlight>
<syntaxhighlight lang="zig">
const std = @import("std");
/// Zig version. When writing code that supports multiple versions of Zig, prefer
/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
pub const zig_version_string = "0.14.0-dev.mos.528+467e65f0c";
pub const zig_backend = std.builtin.CompilerBackend.stage2_llvm;
pub const output_mode = std.builtin.OutputMode.Obj;
pub const link_mode = std.builtin.LinkMode.static;
pub const is_test = false;
pub const single_threaded = false;
pub const abi = std.Target.Abi.eabi;
pub const cpu: std.Target.Cpu = .{
.arch = .mos,
.model = &std.Target.mos.cpu.mos6502,
.features = std.Target.mos.featureSet(&[_]std.Target.mos.Feature{
.mos6502,
.mos_insns_6502,
.mos_insns_6502bcd,
.static_stack,
}),
};
pub const os = std.Target.Os{
.tag = .freestanding,
.version_range = .{ .none = {} },
};
pub const target: std.Target = .{
.cpu = cpu,
.os = os,
.abi = abi,
.ofmt = object_format,
.dynamic_linker = std.Target.DynamicLinker.none,
};
pub const object_format = std.Target.ObjectFormat.elf;
pub const mode = std.builtin.OptimizeMode.Debug;
pub const link_libc = false;
pub const link_libcpp = false;
pub const have_error_return_tracing = true;
pub const valgrind_support = false;
pub const sanitize_thread = false;
pub const position_independent_code = false;
pub const position_independent_executable = false;
pub const strip_debug_info = false;
pub const code_model = std.builtin.CodeModel.default;
pub const omit_frame_pointer = false;
</syntaxhighlight>
=== Other resources ===
*[https://github.com/zigtools/zls Zig language server].
6352998e6b50b2e5116261e1ac7a4d5feb94e5c2
File:Zig mos neo6502.png
6
73
555
2024-11-05T17:32:57Z
Kassane
22
wikitext
text/x-wiki
Using zig-mos on neo6502 emulator
ce4e5e59eaa4f40611fe807c7bb3c2ae79fbde0b
556
555
2024-11-05T17:42:22Z
Kassane
22
Kassane uploaded a new version of [[File:Zig mos neo6502.png]]
wikitext
text/x-wiki
Using zig-mos on neo6502 emulator
ce4e5e59eaa4f40611fe807c7bb3c2ae79fbde0b
557
556
2024-11-05T17:44:13Z
Kassane
22
Kassane reverted [[File:Zig mos neo6502.png]] to an old version
wikitext
text/x-wiki
Using zig-mos on neo6502 emulator
ce4e5e59eaa4f40611fe807c7bb3c2ae79fbde0b
Zero page
0
60
560
479
2025-01-02T16:26:03Z
Asie
12
clarify __zp semantics for pointers
wikitext
text/x-wiki
The default zero-page configurations for the llvm-mos-sdk are generally sufficient for pure C projects; the compiler can automatically make good use of the zero page available on a target. However, hand-written assembly may want to reserve zero page memory from the compiler. Note that mixed C and assembly programmers should acquaint themselves with the llvm-mos [[C calling convention|calling convention]] as well as our FAQs on [[Frequently asked questions|inline assembly]].
The llvm-mos code generator uses the zero page for two purposes: [[imaginary registers]], and as a general store for variables and values. Both are controlled by the [[Linker Script|linker script]], the former by setting symbol values, and the latter by assigning <code>.zp.*</code> sections created by the compiler to the zero page.
For the latter to work, the compiler needs to be made aware of how much contiguous zero page is available. This differs wildly from target to target. Additionally, zero page is very scarce, so the compiler can only safely make use of it if it can achieve a whole-program view of its usage.
Accordingly, zero page allocation is only enabled when compiling with link-time optimization (LTO). The amount available is set by the flag <code>-mlto-zp=''<num_bytes>''</code>, where ''num_bytes'' is the number of contiguous bytes of zero page available, not counting the imaginary registers. Each target in the SDK defaults to consuming the largest available contiguous region of the zero page, and the compiler will automatically make the best use of that region that it can.
User-written assembly routines may also place values in the zero page. Due to the limitations of the structure of a modern compiler, there's no way for the compiler to see into assembly files to extract the number of bytes used. Instead, the compiler must be told ahead of time the number of bytes used by passing the flag <code>-mreserve-zp=''<num_bytes>''</code>. This effectively decreases the number passed to <code>-mlto-zp</code>.
Alternatively, zero page locations can be reserved for assembly use by definining them in LTO-visible C using e.g. <code>char __zp foo;</code>or <code>char __zeropage foo;</code> . The compiler can see these, so it automatically deducts them from the available zero page; the resulting symbols can then be referenced by assembly. This is the approach used in the SDK, since zero page locations for unused routines can be optimized away by the compiler, and pointers to such variables can also be declared to be in the zero page, which allows them to fit in 8 bits. For example, <code>char __zp *fptr = &foo; assert(sizeof(fptr) == 1);</code>.
Note that <code>char __zp *fptr = &foo;</code> declares a pointer ''to'' the zero page (a pointer to ''char __zp'', which is itself not guaranteed to be in the zero page), while <code>char * __zp fptr = &foo;</code> declares a pointer ''in'' the zero page (a pointer in ''__zp'' to ''char''; the value pointed to can be in any memory location, so the pointer is two bytes, but the pointer itself is guaranteed to be in the zero page).
While the above can also be used to force a global to the zero page, this isn't recommended as a default practice. Without the annotation, the compiler may be able to optimize the variable away, perhaps by placing it in a processor register for its lifetime. Failing that, the compiler can already lift global values into the zero page automatically, and it does so based on a whole-program analysis of the loop structure of the program.
[[Category:Code generation]]
[[Category:C]]
73f811b03b0226c4731884629272af2fdf088aeb
Cc65 integration
0
64
562
492
2025-04-21T21:06:15Z
Mysterymath
3
Add LD65 banking information
wikitext
text/x-wiki
LLD has the ability to use od65 and ld65 to link against xo65 object files (the native output format of ca65). LLD searches the system path for these utilities; failing this, their locations are provided by the linker arguments <code>--od65-path</code> and <code>--ld65-path</code>. Using these utilities allows symbol values to pass freely between xo65 object files and ELF object files. xo65 segments are placed as if they were ELF sections. Since ld65's configuration script does not provide the ability to place segments from different xo65 input files differently, all sections derived from xo65 segments are considered in LLD linker scripts to come from a fictitious input file named <code>xo65-enclave</code>. The LLVM-MOS SDK already places the default cc65 segments into the corresponding modern output section.
This feature cannot be used during a relocatable link (<code>-r</code>), since the output is a relocatable ELF file, and ca65's relocations are much richer than LLVM-MOS ELF allows.
The ld65 link step takes the second highest byte of LLD's 32-bit virtual addresses for ca65 segments and makes it the bank specifier, accessible with `.BANK`. The highest byte is zeroed. For symbols, the highest two bytes are zeroed. This is only done if no segment contains far or long addresses.
ELF sections have richer names, types, and flags than xo65 segments. Accordingly, the linker uses an underscore-based escaping scheme to decode this information from the xo65 segment names. The default cc65 segments are automatically assigned the correct type and flags.
{| class="wikitable"
|+Escape Sequences
|-
!Sequence!!Result
|-
|<code>__</code>||<code>_</code>
|-
|<code>_d</code>||<code>$</code>
|-
|<code>_h</code>||<code>-</code>
|-
|<code>_p</code>||<code>.</code>
|-
|<code>_xXX</code>
|The byte given by hex string <code>XX</code> (case insensitive)
|-
|<code>_tn</code>||Section type <code>SHT_NOBITS</code> (last wins)
|-
|<code>_tp</code>||Section type <code>SHT_PROGBITS</code> (last wins)
|-
|<code>_fw</code>
|Section flag <code>SHF_WRITE</code>
|-
|<code>_fx</code>||Section flag <code>SHF_EXECINSTR</code>
|}
[[Category:C]]
[[Category:Linking]]
503316c98b59de865b8fb1b4fbb86474be0fe790
C calling convention
0
17
563
463
2025-05-27T22:13:08Z
Jbyrd
1
Major rewrite based on lldb hacking session
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
- Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call. Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
- Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type.
- Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: `0x10 + (N * 2)` for `rcN`.
Each RS register is assigned a DWARF register number: `0x210 + N` for `rsN`.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Design Rationale==
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions (see Davidson and Whalley, "Methods for Saving and Restoring Register Values across Function Calls," Software—Practice and Experience, Vol 21(2), 149-165, February 1991). The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
===Examples===
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write through ''implicit'' 1st arg pointer
|}
===References===
Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.
Software—Practice and Experience, 21(2), 149-165.
RISC-V Calling Convention (for design inspiration)
44dcce922fef3da3eeb60109e57639394c7b5274
564
563
2025-05-27T22:18:20Z
Jbyrd
1
Formatting cleanup
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call.
Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type.
- Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN.
Each RS register is assigned a DWARF register number: 0x210 + N` for rsN.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Examples==
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write through ''implicit'' 1st arg pointer
|}
===References===
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165.
[https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration)
025dfbd31c3d833f94cf43c7e9299657ac2ac5cc
565
564
2025-05-27T22:18:46Z
Jbyrd
1
/* Return Values */
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call.
Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN.
Each RS register is assigned a DWARF register number: 0x210 + N` for rsN.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Examples==
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write through ''implicit'' 1st arg pointer
|}
===References===
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165.
[https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration)
0a8e2bb19f8ccdb22fee1cf41ad95cb5528e642f
566
565
2025-05-28T22:29:20Z
Mysterymath
3
/* Stack and Frame Pointer Usage */
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call.
Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging and stack unwinding.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN.
Each RS register is assigned a DWARF register number: 0x210 + N` for rsN.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Examples==
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write through ''implicit'' 1st arg pointer
|}
===References===
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165.
[https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration)
3291ddf506f3ed9b60816541b1dbc793336b94e9
567
566
2025-05-28T22:30:49Z
Mysterymath
3
Add details for frame pointer usage.
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call.
Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging, stack unwinding, and certain kinds of C99 variable-sized arrays. Use of the frame pointer is affected by `-f[no-]emit-frame-pointer`, and the default is to not emit a frame pointer.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN.
Each RS register is assigned a DWARF register number: 0x210 + N` for rsN.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Examples==
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even very...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...long ones (incl. <code>rc6-7</code>)
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>||<code>A</code>, <code>X</code>
|...imaginary register pairs
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>|| ||<code>A</code>, <code>X</code>
|Assign from left to right
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| || ||
|By members if <code>sizeof</code> ≤ 4
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| || ||
|By pointer if <code>sizeof</code> > 4
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|<code>A, X, rc2-3</code>
|Return members
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|Return via write through ''implicit'' 1st arg pointer
|}
===References===
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165.
[https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration)
e085ca556121643ad587f9f9e018dc0d012377da
568
567
2025-05-28T22:34:53Z
Mysterymath
3
Clarify example table.
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call.
Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging, stack unwinding, and certain kinds of C99 variable-sized arrays. Use of the frame pointer is affected by `-f[no-]emit-frame-pointer`, and the default is to not emit a frame pointer.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN.
Each RS register is assigned a DWARF register number: 0x210 + N` for rsN.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Examples==
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>
!<code>rc6</code>
!<code>rc7</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|
|
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>
|
| ||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...very long ones.
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|
|
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>
|
| ||<code>A</code>, <code>X</code>
|...imaginary register pairs.
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>||
|
| ||<code>A</code>, <code>X</code>
|Assign from left to right.
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| ||
|
| ||
|By members if <code>sizeof</code> ≤ 4.
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|
| ||
|By pointer if <code>sizeof</code> > 4.
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|
|
|<code>A, X, rc2-3</code>
|Return members.
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|
|
|Return via write through ''implicit'' 1st arg pointer.
|}
===References===
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165.
[https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration)
d6c0c51c0a3ad53db0a2547746abd4fe98455292
577
568
2025-07-28T20:09:03Z
Jbyrd
1
/* DWARF Register Numbering */
wikitext
text/x-wiki
This calling convention and ABI, as implemented in LLVM-MOS, is designed to maximize performance and flexibility on the 65xx architecture, which is characterized by a small set of hardware registers and a limited hardware stack. To overcome these limitations, the convention makes extensive use of [[imaginary registers]] mapped to zero page memory, and employs a hybrid approach to register saving and argument passing, inspired by conventions such as RISC-V and informed by research on efficient calling conventions.
This calling convention is inspired by RISC-V and informed by research on efficient register usage and calling conventions. The convention uses a "smarter hybrid" approach, leveraging both caller-saved and callee-saved registers, and is optimized by LLVM’s shrink wrapping and caller save-restore placement optimizations.
== Register Kinds and Their Purposes ==
Registers in the MOS ABI are divided into two main categories: real registers and imaginary registers.
Real registers include the accumulator (A), index registers (X, Y), processor status flags (C, N, V, Z), program counter (PC), stack pointer (S), direct page register (D), and interrupt flag (I).
Imaginary registers are zero page memory locations, divided into 8-bit rc* ('''r'''egister of '''c'''haracter size) registers (rc0 - r31) and 16-bit rs* ('''r'''egister of '''s'''hort size) registers (rs0 - rs15) where each rs register is a little-endian pair of rc registers.
=== Caller-Saved and Callee-Saved Registers===
The following registers are caller-saved: A, X, Y, C, N, V, Z, and RS1 through RS9 (which correspond to RC2 through RC19). A function may freely overwrite any of these registers; it is the caller’s responsibility to preserve their values if needed across a call.
The following registers are callee-saved: PC, S, D, I, RS0 (RC0 and RC1), and RS10 through RS15 (RC20 through RC31). A function may use these registers, but must restore their original values before returning. Callers can rely on these registers being preserved across function calls.
=== Argument Passing ===
Arguments are assigned from left to right, and the return value is assigned in the same manner as the first argument.
Numeric arguments are passed one byte at a time, first in A, then X, then in RC2 through RC15. For example, a 32-bit integer would be split into four bytes, each assigned to the next available register in this sequence.
Pointers are passed using RS1 through RS7, which are 16-bit registers (each composed of two RC registers). This allows for efficient passing of up to seven pointers in registers.
If there are more arguments than available registers, the remaining arguments are passed on the soft stack (the stack implemented in zero page memory, managed by RS0).
==== Aggregate Types ====
Aggregates (structs, arrays, etc.) of 4 bytes or smaller are split into their constituent value types and passed individually, using the same register assignment rules as for basic types. Such types are also returned by value.
Aggregates larger than 4 bytes are passed by pointer. The caller is responsible for allocating the memory for the aggregate and passing a pointer to it as an argument. The callee may freely write to this memory, and the caller must assume its contents are overwritten by the call.
Large aggregates are returned by a pointer passed as an implicit first argument, and the function then returns void.
==== Variable Arguments ====
Variable arguments (those within the ellipsis of a variadic function) are always passed on the soft stack. Named arguments before the ellipsis are passed as usual (registers first, then stack). This distinction means that variadic functions must be prototyped to ensure correct calling convention, as the handling of variable arguments differs from regular arguments.
==== Return Values ====
Return values are assigned in the same manner as the first argument: in A, then X, then RC2–RC15, as needed for the size of the return type. Large aggregates (greater than 4 bytes) are returned via a pointer passed as an implicit first argument, and the function returns void.
=== Stack and Frame Pointer Usage ===
The primary stack for local variables and most ABI-managed data is the soft stack, implemented in zero page and managed by the RS registers. The soft stack pointer is RS0, and the soft frame pointer is RS15. These are used for stack allocation, frame management, and passing overflow arguments.
The hardware stack pointer (S) is used for return addresses and some leaf calls, but is not the primary stack for local variables. Its limited size (256 bytes, page 1) makes it unsuitable for general-purpose stack management. Still, the compiler may use up to 4 bytes of hardware stack for saving/restoring temporary values.
The soft frame pointer (RS15) is used to reference function frames and local variables, enabling source-level debugging, stack unwinding, and certain kinds of C99 variable-sized arrays. Use of the frame pointer is affected by `-f[no-]emit-frame-pointer`, and the default is to not emit a frame pointer.
==DWARF Register Numbering==
Each RC register is assigned a DWARF register number: 0x10 + (N * 2) for rcN.
Each RS register is assigned a DWARF register number: 0x210 + N for rsN.
The hardware registers (A, X, Y, P, SP, PC) are mapped to the first six registers in the GDB remote stub and have their own DWARF numbers. [need to verify this]
==Examples==
The table below illustrates how function arguments are distributed over the ([[Imaginary registers|imaginary]]) registers.
[[Category:C]]
{| class="wikitable"
|-
!Function!!<code>A</code>!!<code>X</code>!!<code>rc2</code>!!<code>rc3</code>!!<code>rc4</code>!!<code>rc5</code>
!<code>rc6</code>
!<code>rc7</code>!!Output
!Comment
|-
|<source lang="c" enclose="none">char f(int a)</source>||<code>a</code>||<code>a</code>|| || || ||
|
|
|<code>A</code>
|Numeric args passed...
|-
|<source lang="c" enclose="none">long f(long a, int b)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>
|
| ||<code>A</code>, <code>X</code>, <code>rc2-3</code>
|...by value. Even...
|-
|<source lang="c" enclose="none">void f(int64_t a)</source>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|<code>a</code>
|
|...very long ones.
|-
|<source lang="c" enclose="none">int * f(void *a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|
|
|<code>rc2-3</code>
|Pointers only through...
|-
|<source lang="c" enclose="none">int f(int a, int b, void *c)</source>||<code>a</code>||<code>a</code>||<code>b</code>||<code>b</code>||<code>c</code>||<code>c</code>
|
| ||<code>A</code>, <code>X</code>
|...imaginary register pairs.
|-
|<source lang="c" enclose="none">int f(void *a, char b, int c)</source>||<code>b</code>||<code>c</code>||<code>a</code>||<code>a</code>||<code>c</code>||
|
| ||<code>A</code>, <code>X</code>
|Assign from left to right.
|-
|<source lang="c" enclose="none">void f(struct div_t a)</source>||<code>a</code>||<code>a</code>||<code>a</code>||<code>a</code>|| ||
|
| ||
|By members if <code>sizeof</code> ≤ 4.
|-
|<source lang="c" enclose="none">void f(struct ldiv_t a)</source>|| || ||<code>a</code>||<code>a</code>|| ||
|
| ||
|By pointer if <code>sizeof</code> > 4.
|-
|<source lang="c" enclose="none">div_t f(void *a)</source>
|
|
|<code>a</code>
|<code>a</code>
|
|
|
|
|<code>A, X, rc2-3</code>
|Return members.
|-
|<source lang="c" enclose="none">ldiv_t f(void *a)</source>
|
|
|👻
|👻
|<code>a</code>
|<code>a</code>
|
|
|
|Return via write through ''implicit'' 1st arg pointer.
|}
===References===
[https://www.cs.fsu.edu/~whalley/papers/spe91.pdf Davidson, J. W., & Whalley, D. B. (1991). Methods for Saving and Restoring Register Values across Function Calls.] Software—Practice and Experience, 21(2), 149-165.
[https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf RISC-V Calling Convention] (for design inspiration)
27d25ddf5c78566f3766b1407537b2ecd9097d5c
File:Linux-c64.jpg
6
74
569
2025-07-27T19:02:51Z
Jbyrd
1
wikitext
text/x-wiki
Linux booting on a Commodore 64 with REU
5040e3b881cc5ae23671b335e58f6affc0462f6c
Welcome
0
1
570
513
2025-07-27T19:04:30Z
Jbyrd
1
Add Linux on C64
wikitext
text/x-wiki
[[Category:Main]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Hello-c64.png|thumb|Hello world of LLVM assembler targeting Commodore 64]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
640b4b75be1439ac9caf3340bb53da93ab4e90d4
572
570
2025-07-27T19:10:04Z
Jbyrd
1
Add game GIF
wikitext
text/x-wiki
[[Category:Main]]
[[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
==Welcome to the llvm-mos project!==
[[File:LLVM-MOS logo.png|alt=8-bit LLVM dragon logo|thumb]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
4a0c40ed714e8706acaacf866d312688f802b094
573
572
2025-07-28T19:22:45Z
Jbyrd
1
Remove header to make mobile users happier
wikitext
text/x-wiki
[[Category:Main]]
[[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
llvm-mos is a fork of [https://llvm.org/ LLVM] supporting the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and their clones.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
c38e30e3c7dfdf1eb16bb216ad72a5df85283dd8
574
573
2025-07-28T19:34:05Z
Jbyrd
1
Rewrote first paragraph
wikitext
text/x-wiki
[[Category:Main]]
[[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables software development for many classic platforms.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners Github runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
bc498d4ca310772bb94645d2908a63e77e8179c1
575
574
2025-07-28T19:35:11Z
Jbyrd
1
Tweak content
wikitext
text/x-wiki
[[Category:Main]]
[[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables software development for many classic platforms.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners GitHub runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
91cdbe71f670e4200b5e3d18849034bbaf48edc2
576
575
2025-07-28T19:35:44Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables modern software development for many classic platforms.
Compiler Explorer currently supports the llvm-mos toolchain, so you can experiment with the compiler without downloading anything. Here's a [https://godbolt.org/z/WGa169Kve simple Commodore 64 project] that shows the compiler in action.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners GitHub runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
83901a6b5207d8cf4111a04faa56eea3b982154d
587
576
2025-10-02T15:25:05Z
Jbyrd
1
wikitext
text/x-wiki
[[Category:Main]]
[[File:Miroh Jr for NES.gif|thumb|Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS]]
[[File:Linux-c64.jpg|thumb|Linux booting on Commodore 64 with REU, via emulator built with LLVM-MOS]]
[[File:Nes-llvm-demo.png|thumb|NES project in pure C99 using llvm-mos, by Steven Poulton]]
[[File:Rust-hello-atari-800.png|thumb|Hello world in Rust, with factorial calculation, for Atari 800, proof of concept by mrk]]
[[File:Hello-vic20.png|thumb|Hello world of LLVM assembler targeting Commodore VIC-20]]
[[File:Hello-apple2.png|thumb|Hello world of LLVM assembler targeting Apple IIe]]
[[File:Llvm-mos hello world osi c1p.png|thumb|Hello world in C, targeting Ohio Scientific Challenger 1P, port by Stephan Mühlstrasser]]
[[File:Hello Commander X16.png|thumb|Hello Commander X16 from C]]
llvm-mos is an open-source fork of the [https://llvm.org/ LLVM] C/C++ compiler, that provides first-class support to the [[wikipedia:MOS_Technology|MOS Technology]] 65xx series of microprocessors and relatives. llvm-mos enables modern software development for many classic platforms.
You can play with the compiler right now on [https://godbolt.org/z/c11Th3oMW Godbolt]! Here's a [https://godbolt.org/z/c11Th3oMW Commodore 64 project] that shows the C compiler in action. Try the "Click here to emulate" link on that page, to watch a Commodore 64 compute a hundred digits of pi.
To download and use the tools locally, check out [https://github.com/llvm-mos/llvm-mos-sdk#getting-started Getting started].
Our work encompasses the following:
* An open-source [https://github.com/llvm-mos/llvm-mos fork of LLVM] that provides first-class support to the [[wikipedia:MOS_Technology_6502|MOS 6502]] and near compatibles. Our compiler implements [https://www.youtube.com/watch?v=2lW3WHPtmKo multiple novel approaches] in order to optimize 6502 code size and speed, and it tends to outperform legacy 6502 compilers. Our compiler includes, but is not limited to:
** broad C99 and C++11 freestanding compatibility
** built-in [https://github.com/llvm-mos/llvm-mos/issues/10 IEEE-754 floating point support]
** first-class [[Assembler|integrated 65xx assembler support]]
** clang's world-class error messages
* An open-source [https://github.com/llvm-mos/llvm-mos-sdk software development kit] (SDK) that comprises target-specific code for common 65xx-based microcomputers. It includes a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform/common minimal C standard library] with support for memory management and text output. Some [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/examples example programs] are included. We currently support [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/mos-platform over two dozen targets], including a [https://github.com/llvm-mos/llvm-mos-sdk/tree/main/utils/sim host-based simulator]. The list of supported targets is growing rapidly.
* [https://llvm-mos.org/wiki/ELF_specification Complete ELF support] for the 6502 series, which permits us to use the existing [https://github.com/llvm-mos/llvm-mos/blob/main/clang/cmake/caches/MOS.cmake#L36 suite of LLVM tools] for creating, analyzing and modifying 6502 object files and libraries.
* An open-source automated [https://github.com/llvm-mos/llvm-test-suite test] and [https://github.com/llvm-mos/llvm-mos-sdk/blob/main/.github/workflows/package.yml packaging] infrastructure. We use [https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners GitHub runners] to verify builds of our compiler against appropriate LLVM test suites, as well as our own [https://github.com/llvm-mos/smoke-test smoke tests]. We also run [https://llvm-mos.github.io/llvm-test-suite automated benchmarks] using this infrastructure.
Ongoing public development discussions occur on Discord. If you'd like to help, then [https://discord.gg/D9gRm3aznh please join our Discord group now].
====Notice====
The llvm-mos project is not officially affiliated with or endorsed by the LLVM Foundation. Our project is a fork of LLVM that provides a new backend and Clang target; our project is based on LLVM, not a part of LLVM. Our use of LLVM or other related trademarks does not imply affiliation or endorsement.
===Category tree===
<categorytree mode="pages" depth="3" hideroot="on">Main</categorytree>
===Categories===
{{Special:AllPages|namespace=14}}
===Pages===
{{Special:AllPages}}
bad00805f8411912c0d9d83a98b42d82885941fa
File:Miroh Jr for NES.gif
6
75
571
2025-07-27T19:09:11Z
Jbyrd
1
wikitext
text/x-wiki
Miroh Jr for NES, by Wendel, Raftronaut, and M-Tee, built with LLVM-MOS
82ad901bc112134cb69d0fa6da89f0a0fce63fc5
DWARF specification
0
76
578
2025-09-28T07:52:52Z
Jbyrd
1
Initial implementation.
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
'''Version 0.1.0'''
Comments and improvements are solicited at johnwbyrd at gmail dot com.
<span id="summary-for-implementers"></span>
== Summary for Implementers ==
DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
* Are implementing comprehensive debugging support for the broader 6502 ecosystem
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
<span id="overview"></span>
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
<span id="terminology"></span>
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor Register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280’s memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF.
'''Chipset Register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB Subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
<span id="register-number-format"></span>
== Register Number Format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
<span id="processor-registers-vs.-chipset-registers"></span>
=== Processor Registers vs. Chipset Registers ===
The fundamental distinction in this specification is between processor registers and chipset registers:
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU’s architectural state.
<span id="processor-register-formats-0x00000000-0x7fffffff"></span>
=== Processor Register Formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
<span id="structured-format"></span>
==== Structured Format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat Format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
<span id="chipset-registers-0x80000000-0xffffffff"></span>
=== Chipset Registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="format-selection-by-family"></span>
=== Format Selection by Family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
<span id="core-mos-processor-register-assignments"></span>
== Core MOS Processor Register Assignments ==
<span id="family-0x00-mos-6502-family"></span>
=== Family 0x00: MOS 6502 Family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion.
<span id="type-0x00-physical-registers"></span>
==== Type 0x00: Physical Registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
<span id="type-0x01-reserved"></span>
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
<span id="type-0x02-rc-imaginary-registers-8-bit"></span>
==== Type 0x02: RC Imaginary Registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
<span id="type-0x03-rs-imaginary-registers-16-bit"></span>
==== Type 0x03: RS Imaginary Registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
<span id="type-0x04-rl-imaginary-registers-32-bit"></span>
==== Type 0x04: RL Imaginary Registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
<span id="type-0x05-rq-imaginary-registers-64-bit"></span>
==== Type 0x05: RQ Imaginary Registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
<span id="types-0x06-0x7f-reserved"></span>
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
<span id="type-0x10-huc6280-extensions"></span>
==== Type 0x10: HuC6280 Extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
<span id="type-0x11-65108502-extensions"></span>
==== Type 0x11: 6510/8502 Extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
<span id="type-0x12-ricoh-2a032a07-extensions"></span>
==== Type 0x12: Ricoh 2A03/2A07 Extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
<span id="type-0x13-65ce02-extensions"></span>
==== Type 0x13: 65CE02 Extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
<span id="type-0x14-w65c02s-extensions"></span>
==== Type 0x14: W65C02S Extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
<span id="types-0x15-0x7f-reserved"></span>
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
<span id="types-0x80-0xff-banking-and-memory-management"></span>
==== Types 0x80-0xFF: Banking and Memory Management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
<span id="family-0x01-65816-native-mode"></span>
=== Family 0x01: 65816 Native Mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
<span id="type-0x00-native-mode-registers"></span>
==== Type 0x00: Native Mode Registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
<span id="family-0x02-virtual-machines"></span>
=== Family 0x02: Virtual Machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
<span id="type-0x00-sweet-16"></span>
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
<span id="type-0x01-reserved-for-bbc-vm"></span>
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
<span id="types-0x02-0xff-reserved"></span>
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
<span id="families-0x03-0x7f-reserved"></span>
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
<span id="family-0x03-spc700"></span>
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
<span id="type-0x00-core-registers"></span>
==== Type 0x00: Core Registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
<span id="chipset-register-assignments"></span>
== Chipset Register Assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform Organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic Chipset Platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O Controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common Sound Processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common Video Controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-Specific Chipset Platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended Register Concepts ==
<span id="banking-support"></span>
=== Banking Support ===
Memory banking schemes receive their own type identifiers within a family’s allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB Subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation Notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM’s initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
60edaa91b7b19f11bd9e13176e7b818bbab232da
579
578
2025-09-28T07:53:58Z
Jbyrd
1
/* DWARF Specification for MOS-Compatible Processors */
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
'''Version 0.1.0'''
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for Implementers ==
DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
* Are implementing comprehensive debugging support for the broader 6502 ecosystem
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
<span id="overview"></span>
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
<span id="terminology"></span>
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor Register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280’s memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF.
'''Chipset Register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB Subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
<span id="register-number-format"></span>
== Register Number Format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
<span id="processor-registers-vs.-chipset-registers"></span>
=== Processor Registers vs. Chipset Registers ===
The fundamental distinction in this specification is between processor registers and chipset registers:
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU’s architectural state.
<span id="processor-register-formats-0x00000000-0x7fffffff"></span>
=== Processor Register Formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
<span id="structured-format"></span>
==== Structured Format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat Format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
<span id="chipset-registers-0x80000000-0xffffffff"></span>
=== Chipset Registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="format-selection-by-family"></span>
=== Format Selection by Family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
<span id="core-mos-processor-register-assignments"></span>
== Core MOS Processor Register Assignments ==
<span id="family-0x00-mos-6502-family"></span>
=== Family 0x00: MOS 6502 Family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion.
<span id="type-0x00-physical-registers"></span>
==== Type 0x00: Physical Registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
<span id="type-0x01-reserved"></span>
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
<span id="type-0x02-rc-imaginary-registers-8-bit"></span>
==== Type 0x02: RC Imaginary Registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
<span id="type-0x03-rs-imaginary-registers-16-bit"></span>
==== Type 0x03: RS Imaginary Registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
<span id="type-0x04-rl-imaginary-registers-32-bit"></span>
==== Type 0x04: RL Imaginary Registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
<span id="type-0x05-rq-imaginary-registers-64-bit"></span>
==== Type 0x05: RQ Imaginary Registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
<span id="types-0x06-0x7f-reserved"></span>
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
<span id="type-0x10-huc6280-extensions"></span>
==== Type 0x10: HuC6280 Extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
<span id="type-0x11-65108502-extensions"></span>
==== Type 0x11: 6510/8502 Extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
<span id="type-0x12-ricoh-2a032a07-extensions"></span>
==== Type 0x12: Ricoh 2A03/2A07 Extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
<span id="type-0x13-65ce02-extensions"></span>
==== Type 0x13: 65CE02 Extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
<span id="type-0x14-w65c02s-extensions"></span>
==== Type 0x14: W65C02S Extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
<span id="types-0x15-0x7f-reserved"></span>
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
<span id="types-0x80-0xff-banking-and-memory-management"></span>
==== Types 0x80-0xFF: Banking and Memory Management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
<span id="family-0x01-65816-native-mode"></span>
=== Family 0x01: 65816 Native Mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
<span id="type-0x00-native-mode-registers"></span>
==== Type 0x00: Native Mode Registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
<span id="family-0x02-virtual-machines"></span>
=== Family 0x02: Virtual Machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
<span id="type-0x00-sweet-16"></span>
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
<span id="type-0x01-reserved-for-bbc-vm"></span>
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
<span id="types-0x02-0xff-reserved"></span>
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
<span id="families-0x03-0x7f-reserved"></span>
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
<span id="family-0x03-spc700"></span>
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
<span id="type-0x00-core-registers"></span>
==== Type 0x00: Core Registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
<span id="chipset-register-assignments"></span>
== Chipset Register Assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform Organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic Chipset Platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O Controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common Sound Processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common Video Controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-Specific Chipset Platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended Register Concepts ==
<span id="banking-support"></span>
=== Banking Support ===
Memory banking schemes receive their own type identifiers within a family’s allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB Subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation Notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM’s initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
852a09fcb1acd28affb6fc404604e29951f18f49
580
579
2025-09-28T07:58:20Z
Jbyrd
1
/* DWARF Specification for MOS-Compatible Processors */
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
Version 0.1.0
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for implementers ==
DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
* Are implementing comprehensive debugging support for the broader 6502 ecosystem
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor Register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280’s memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF.
'''Chipset Register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB Subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
== Register number format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
=== Processor registers vs. chipset registers ===
The fundamental distinction in this specification is between processor registers and chipset registers:
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU’s architectural state.
=== Processor register formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
==== Structured format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat Format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
=== Chipset registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
=== Format selection by family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
== Core MOS processor register assignments ==
=== Family 0x00: MOS 6502 family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion.
==== Type 0x00: Physical registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
==== Type 0x02: RC imaginary registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
==== Type 0x03: RS imaginary registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
==== Type 0x04: RL imaginary registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
==== Type 0x05: RQ imaginary registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
==== Type 0x10: HuC6280 extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
==== Type 0x11: 6510/8502 extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
==== Type 0x12: Ricoh 2A03/2A07 extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
==== Type 0x13: 65CE02 extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
==== Type 0x14: W65C02S extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
==== Types 0x80-0xFF: Banking and memory management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
=== Family 0x01: 65816 native mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
==== Type 0x00: Native mode registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
=== Family 0x02: Virtual machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
==== Type 0x00: Core Registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
== Chipset register assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform Organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic Chipset Platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O Controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common Sound Processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common Video Controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-Specific Chipset Platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo Systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended Register Concepts ==
<span id="banking-support"></span>
=== Banking Support ===
Memory banking schemes receive their own type identifiers within a family’s allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB Subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation Notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM’s initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
477bd5bea57279f8ad2800866f5bd3038adadc06
581
580
2025-09-28T08:02:24Z
Jbyrd
1
Reformat to Sentence case
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
Version 0.1.0
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for implementers ==
DWARF is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in GDB, LLDB, or other debuggers, they use DWARF data to map between your source code and the machine code. For retro developers, DWARF enables source-level debugging of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
* Are implementing comprehensive debugging support for the broader 6502 ecosystem
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF.
'''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
== Register number format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
=== Processor registers vs. chipset registers ===
The fundamental distinction in this specification is between processor registers and chipset registers:
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state.
=== Processor register formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
==== Structured format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
=== Chipset registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
=== Format selection by family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
== Core MOS processor register assignments ==
=== Family 0x00: MOS 6502 family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion.
==== Type 0x00: Physical registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
==== Type 0x02: RC imaginary registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
==== Type 0x03: RS imaginary registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
==== Type 0x04: RL imaginary registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
==== Type 0x05: RQ imaginary registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
==== Type 0x10: HuC6280 extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
==== Type 0x11: 6510/8502 extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
==== Type 0x12: Ricoh 2A03/2A07 extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
==== Type 0x13: 65CE02 extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
==== Type 0x14: W65C02S extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
==== Types 0x80-0xFF: Banking and memory management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
=== Family 0x01: 65816 native mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
==== Type 0x00: Native mode registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
=== Family 0x02: Virtual machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
==== Type 0x00: Core registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
== Chipset register assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic chipset platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common sound processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common video controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-specific chipset platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended register concepts ==
<span id="banking-support"></span>
=== Banking support ===
Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
7476c8a99c557b29d94b1e0dda3d63e580dc54ef
583
581
2025-09-28T08:13:29Z
Jbyrd
1
Lots of links added
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
Version 0.1.0
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for implementers ==
[[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy addresses 0x00000000-0x7FFFFFFF.
'''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy addresses 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
== Register number format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
=== Processor registers vs. chipset registers ===
The fundamental distinction in this specification is between processor registers and chipset registers:
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state.
=== Processor register formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
==== Structured format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
=== Chipset registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
=== Format selection by family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
== Core MOS processor register assignments ==
=== Family 0x00: MOS 6502 family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion.
==== Type 0x00: Physical registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
==== Type 0x02: RC imaginary registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
==== Type 0x03: RS imaginary registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
==== Type 0x04: RL imaginary registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
==== Type 0x05: RQ imaginary registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
==== Type 0x10: HuC6280 extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
==== Type 0x11: 6510/8502 extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
==== Type 0x12: Ricoh 2A03/2A07 extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
==== Type 0x13: 65CE02 extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
==== Type 0x14: W65C02S extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
==== Types 0x80-0xFF: Banking and memory management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
=== Family 0x01: 65816 native mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
==== Type 0x00: Native mode registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
=== Family 0x02: Virtual machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
==== Type 0x00: Core registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
== Chipset register assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic chipset platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common sound processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common video controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-specific chipset platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended register concepts ==
<span id="banking-support"></span>
=== Banking support ===
Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
92e62009d7a20e4aebfe94f12d4e0573e1e487fd
584
583
2025-09-28T08:16:13Z
Jbyrd
1
Clearer description of DWARF numbering vs addresses
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
Version 0.1.0
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for implementers ==
[[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy DWARF numbers 0x00000000-0x7FFFFFFF.
'''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy DWARF numbers 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
== Register number format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state.
=== Processor register formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
==== Structured format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
=== Chipset registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
=== Format selection by family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
== Core MOS processor register assignments ==
=== Family 0x00: MOS 6502 family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has generous reserved space for future expansion.
==== Type 0x00: Physical registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
==== Type 0x02: RC imaginary registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
==== Type 0x03: RS imaginary registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
==== Type 0x04: RL imaginary registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
==== Type 0x05: RQ imaginary registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
==== Type 0x10: HuC6280 extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
==== Type 0x11: 6510/8502 extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
==== Type 0x12: Ricoh 2A03/2A07 extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
==== Type 0x13: 65CE02 extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
==== Type 0x14: W65C02S extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
==== Types 0x80-0xFF: Banking and memory management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
=== Family 0x01: 65816 native mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
==== Type 0x00: Native mode registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
=== Family 0x02: Virtual machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
==== Type 0x00: Core registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
== Chipset register assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic chipset platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common sound processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common video controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-specific chipset platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended register concepts ==
<span id="banking-support"></span>
=== Banking support ===
Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
4f7c57560b855859a320593434b4bd7bfba24d26
585
584
2025-09-28T08:16:35Z
Jbyrd
1
/* Family 0x00: MOS 6502 family */
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
Version 0.1.0
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for implementers ==
[[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy DWARF numbers 0x00000000-0x7FFFFFFF.
'''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy DWARF numbers 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
== Register number format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state.
=== Processor register formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
==== Structured format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
=== Chipset registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
=== Format selection by family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
== Core MOS processor register assignments ==
=== Family 0x00: MOS 6502 family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has space reserved for future expansion.
==== Type 0x00: Physical registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006
| S
| Stack Register (LLVM-MOS)
|-
| 0x00000007
| D
| Direct Page Register (65816 emulation)
|-
| 0x00000008-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
==== Type 0x02: RC imaginary registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
==== Type 0x03: RS imaginary registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
==== Type 0x04: RL imaginary registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
==== Type 0x05: RQ imaginary registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
==== Type 0x10: HuC6280 extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
==== Type 0x11: 6510/8502 extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
==== Type 0x12: Ricoh 2A03/2A07 extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
==== Type 0x13: 65CE02 extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
==== Type 0x14: W65C02S extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
==== Types 0x80-0xFF: Banking and memory management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
=== Family 0x01: 65816 native mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode.
==== Type 0x00: Native mode registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
=== Family 0x02: Virtual machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
==== Type 0x00: Core registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
== Chipset register assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic chipset platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common sound processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common video controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-specific chipset platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended register concepts ==
<span id="banking-support"></span>
=== Banking support ===
Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
e0d88e287660597b1d7352c111607d65068bb7e7
586
585
2025-09-28T08:21:58Z
Jbyrd
1
/* Type 0x00: Physical registers */
wikitext
text/x-wiki
<span id="dwarf-specification-for-mos-compatible-processors"></span>
= DWARF Specification for MOS-Compatible Processors =
Version 0.1.0
Comments and improvements are solicited at johnwbyrd at gmail dot com.
== Summary for implementers ==
[[wikipedia:DWARF|DWARF]] is the standard debugging data format used by modern compilers and debuggers. When you set a breakpoint, inspect variables, or step through code in [[wikipedia:GNU_Debugger|GDB]], [[wikipedia:LLDB_(debugger)|LLDB]], or other debuggers, they use DWARF data to map between your source code and the machine code. DWARF information, as stored in an [[ELF overview|ELF file]], enables [https://llvm.org/docs/SourceLevelDebugging.html source-level debugging] of C, Rust, and assembly code on 6502 systems. Without standardized DWARF register numbers, different tools might disagree about which register is which, making debugging needlessly difficult.
Most 6502 debugger and emulator developers only need these six DWARF register numbers:
{| class="wikitable"
|-
! DWARF Number
! Register
! Description
|-
| 0
| A
| Accumulator
|-
| 1
| X
| X Index Register
|-
| 2
| Y
| Y Index Register
|-
| 3
| P
| Processor Status Register
|-
| 4
| SP
| Stack Pointer
|-
| 5
| PC
| Program Counter
|}
These numbers are compatible with MAME and most existing 6502 debugging tools.
'''You should read the full specification if you:'''
* Want to implement source-level debugging for your compiler, emulator, or MOS-compatible thing
* Are implementing LLVM-MOS or another compiler that uses imaginary registers
* Need to support 6502 variants with extended registers (65816, HuC6280, 65CE02)
* Are working with NES mappers or other chipset-specific registers
* Want to avoid register number collisions in complex debugging scenarios
The full specification defines a structured 32-bit numbering scheme that prevents collisions between different register types, processor variants, and peripheral chips while maintaining backward compatibility with existing tools.
== Overview ==
This specification extends the DWARF Debugging Information Format to encompass the MOS family of processors and their relatives. These include, but are not limited to, the MOS 6502, the MOS 65816, and comparable processors, as well as virtual machines and co-processors that operate within the MOS ecosystem.
This specification considers the following documents to be normative, and incorporates them by reference:
* [https://datatracker.ietf.org/doc/html/rfc2119 Key words for use in RFCs to Indicate Requirement Levels]
* [https://dwarfstd.org/doc/DWARF5.pdf DWARF Debugging Information Format Version 5]
* [https://llvm-mos.org/wiki/ELF_specification ELF specification for MOS-compatible processors]
* [https://semver.org/ Semantic Versioning 2.0.0]
== Terminology ==
This specification uses specific terminology to organize the DWARF register number space:
'''Processor register''': A register that is part of the CPU itself or directly controlled by CPU instructions. This includes physical CPU registers (A, X, Y, SP, PC), status flags, imaginary registers allocated in zero page for compiler optimization, and CPU-integrated peripherals like the HuC6280's memory mapping registers. Processor registers occupy DWARF numbers 0x00000000-0x7FFFFFFF.
'''Chipset register''': A register belonging to a separate chip or peripheral device that communicates with the CPU through memory-mapped I/O. Examples include video controllers (VIC-II), sound chips (SID), and I/O controllers (6522 VIA). Chipset registers occupy DWARF numbers 0x80000000-0xFFFFFFFF and are organized by platform and specific chip.
'''Family''': A processor architecture family such as the 6502, 65816, or a virtual machine architecture. The family number (bits 31-24) determines both the processor variant and how the remaining 24 bits are interpreted—either as structured format (type + register) or flat format (direct register number).
'''Type''': A category of registers within a processor family, such as physical registers, status flags, or imaginary registers. Types only exist in the structured register format.
'''Platform''': A computer system manufacturer or category, used for organizing chipset registers. Examples include Commodore, Apple, Atari, or Generic for widely-used chips.
'''Chipset''': A specific hardware component within a platform, such as the VIC-II graphics chip in Commodore systems or the 6522 VIA in generic platforms.
'''Register''': An individual register within its containing type or chipset.
'''LSB subregister''': A 1-bit subregister representing the least significant bit of an 8-bit register.
== Register number format ==
DWARF register numbers use a 32-bit structured format. The highest bit determines whether the number represents a processor register or a chipset register.
'''Processor registers''' (bit 31 = 0) include all registers that are part of the CPU architecture or tightly integrated with it. This encompasses CPU registers accessible through machine instructions, compiler-generated imaginary registers in zero page, and CPU-integrated peripherals. These registers are essential to the instruction stream and program execution.
'''Chipset registers''' (bit 31 = 1) represent memory-mapped registers of external chips that communicate with the CPU via the bus. These include video chips, sound chips, I/O controllers, and other peripherals. While programs interact with these registers, they are not part of the CPU's architectural state.
=== Processor register formats (0x00000000-0x7FFFFFFF) ===
The family number (bits 31-24) determines which format is used for the remaining 24 bits. Each processor family specifies whether it uses structured or flat format.
==== Structured format ====
Used when a processor family needs to organize registers into distinct types:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-16
| Type
| Register type within family
| 0x00-0xFF (256 types)
|-
| 15-0
| Register
| Register number within type
| 0x0000-0xFFFF (65,536 registers)
|}
<span id="flat-format"></span>
==== Flat format ====
Used when a processor family needs a simple, large register space:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31-24
| Family
| Processor architecture family
| 0x00-0x7F (128 families)
|-
| 23-0
| Register
| Register number
| 0x000000-0xFFFFFF (16,777,216 registers)
|}
=== Chipset registers (0x80000000-0xFFFFFFFF) ===
Chipset registers always use a structured format:
{| class="wikitable"
|-
! Bits
! Field
! Description
! Range
|-
| 31
| Chipset Flag
| Always 1
| Identifies chipset space
|-
| 30-24
| Platform
| Computer platform
| 0x00-0x7F (128 platforms)
|-
| 23-16
| Chipset
| Chip within platform
| 0x00-0xFF (256 chips)
|-
| 15-0
| Register
| Register within chipset
| 0x0000-0xFFFF (65,536 registers)
|}
=== Format selection by family ===
Each processor family defines its register format:
{| class="wikitable"
|-
! Family
! Name
! Format
! Rationale
|-
| 0x00
| MOS 6502
| Structured
| Multiple register types: physical, flags, imaginary (RC, RS, RL, RQ)
|-
| 0x01
| 65816 Native
| Structured
| Distinct types for native mode features and banking
|-
| 0x02
| Virtual Machines
| Structured
| Different VMs as different types
|-
| 0x03
| SPC700
| Structured
| Different architecture with PSW and YA register
|-
| 0x04-0x7F
| Reserved
| Family-defined
| Future families declare their format
|}
== Core MOS processor register assignments ==
=== Family 0x00: MOS 6502 family ===
The MOS 6502 family uses the structured format with multiple register types. Each type has space reserved for future expansion.
==== Type 0x00: Physical registers ====
Physical registers include the core processor registers and extensions. The layout provides compatibility with existing tools while reserving space for future additions.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00000000
| A
| Accumulator
|-
| 0x00000001
| X
| X Index Register
|-
| 0x00000002
| Y
| Y Index Register
|-
| 0x00000003
| P
| Processor Status Register
|-
| 0x00000004
| SP
| Stack Pointer
|-
| 0x00000005
| PC
| Program Counter
|-
| 0x00000006-0x000000FF
| -
| Reserved for future physical registers
|-
| 0x00000100
| ALSB
| Accumulator LSB
|-
| 0x00000101
| XLSB
| X Index LSB
|-
| 0x00000102
| YLSB
| Y Index LSB
|-
| 0x00000103
| SLSB
| Stack Register LSB
|-
| 0x00000104-0x000001FF
| -
| Reserved for future LSB registers
|-
| 0x00000200
| C
| Carry Flag
|-
| 0x00000201
| Z
| Zero Flag
|-
| 0x00000202
| I
| Interrupt Disable Flag
|-
| 0x00000203
| D
| Decimal Mode Flag
|-
| 0x00000204
| B
| Break Flag
|-
| 0x00000205
| V
| Overflow Flag
|-
| 0x00000206
| N
| Negative Flag
|-
| 0x00000207
| NZ
| Combined N and Z flags
|-
| 0x00000208-0x000002FF
| -
| Reserved for future flag registers
|-
| 0x00000300
| Fake
| Pseudo-register for unsupported types
|-
| 0x00000301-0x00000FFF
| -
| Reserved for future special registers
|-
| 0x00001000-0x0000FFFF
| -
| Reserved for future physical extensions
|}
==== Type 0x01: Reserved ====
The entire type 0x01 space (0x00010000-0x0001FFFF) is reserved for future use.
==== Type 0x02: RC imaginary registers (8-bit) ====
LLVM-MOS uses 8-bit imaginary registers for zero page allocation. Each RC register has an associated LSB subregister for boolean operations.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00020000-0x000200FF
| RC0-RC255
| 256 8-bit imaginary registers
|-
| 0x00020100-0x000201FF
| -
| Reserved
|-
| 0x00021000-0x000210FF
| RC0LSB-RC255LSB
| LSB subregisters for RC registers
|-
| 0x00021100-0x0002FFFF
| -
| Reserved for future RC expansion
|}
==== Type 0x03: RS imaginary registers (16-bit) ====
Sixteen-bit imaginary registers are used as pointer pairs. Each RS register consists of two consecutive RC registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00030000-0x0003007F
| RS0-RS127
| 128 16-bit imaginary registers
|-
| 0x00030080-0x0003FFFF
| -
| Reserved for future RS expansion
|}
==== Type 0x04: RL imaginary registers (32-bit) ====
Thirty-two-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00040000-0x0004001F
| RL0-RL31
| 32 32-bit imaginary registers
|-
| 0x00040020-0x000400FF
| -
| Reserved
|-
| 0x00041000-0x000410FF
| RL0LSB-RL31LSB
| LSB subregisters for RL registers
|-
| 0x00041100-0x0004FFFF
| -
| Reserved for future RL expansion
|}
==== Type 0x05: RQ imaginary registers (64-bit) ====
Sixty-four-bit imaginary registers for future expansion.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00050000-0x0005000F
| RQ0-RQ15
| 16 64-bit imaginary registers
|-
| 0x00050010-0x000500FF
| -
| Reserved
|-
| 0x00051000-0x0005100F
| RQ0LSB-RQ15LSB
| LSB subregisters for RQ registers
|-
| 0x00051010-0x0005FFFF
| -
| Reserved for future RQ expansion
|}
==== Types 0x06-0x7F: Reserved ====
Types 0x06 through 0x7F are reserved for future MOS 6502 family extensions.
==== Type 0x10: HuC6280 extensions ====
The HuC6280 (PC Engine/TurboGrafx-16) adds memory mapping registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00100000
| MPR0
| Memory Page Register 0
|-
| 0x00100001
| MPR1
| Memory Page Register 1
|-
| 0x00100002
| MPR2
| Memory Page Register 2
|-
| 0x00100003
| MPR3
| Memory Page Register 3
|-
| 0x00100004
| MPR4
| Memory Page Register 4
|-
| 0x00100005
| MPR5
| Memory Page Register 5
|-
| 0x00100006
| MPR6
| Memory Page Register 6
|-
| 0x00100007
| MPR7
| Memory Page Register 7
|-
| 0x00100008
| TIMER_CNT
| Timer Counter
|-
| 0x00100009
| TIMER_CTRL
| Timer Control
|-
| 0x0010000A-0x0010FFFF
| -
| Reserved
|}
==== Type 0x11: 6510/8502 extensions ====
The 6510 (C64) and 8502 (C128) include integrated I/O port functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00110000
| DDR
| Data Direction Register (location 0x00)
|-
| 0x00110001
| PORT
| I/O Port Register (location 0x01)
|-
| 0x00110002-0x0011FFFF
| -
| Reserved
|}
==== Type 0x12: Ricoh 2A03/2A07 extensions ====
The Ricoh 2A03 (NES NTSC) and 2A07 (NES PAL) integrate DMA functionality.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00120000
| OAMDMA
| OAM DMA Register
|-
| 0x00120001-0x0012FFFF
| -
| Reserved
|}
==== Type 0x13: 65CE02 extensions ====
The 65CE02 (Commodore 65) extends the 65C02 with additional registers.
{| class="wikitable"
|-
! Address Range
! Registers
! Description
|-
| 0x00130000
| B
| Base Page Register
|-
| 0x00130001
| Z
| Z Index Register
|-
| 0x00130002
| SPH
| Stack Pointer High
|-
| 0x00130003-0x0013FFFF
| -
| Reserved
|}
==== Type 0x14: W65C02S extensions ====
Reserved for W65C02S-specific registers like WAI and STP states (0x00140000-0x0014FFFF).
==== Types 0x15-0x7F: Reserved ====
Reserved for other 6502 variant extensions.
==== Types 0x80-0xFF: Banking and memory management ====
These types support banking schemes where each distinct banking configuration receives its own type identifier.
=== Family 0x01: 65816 native mode ===
The 65816 in native mode uses family identifier 0x01 to distinguish its 16-bit registers from the 8-bit registers of emulation mode. Whether these registers are punned to the underlying 8-bit registers in 6502 compatible mode is implementation defined.
==== Type 0x00: Native mode registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x01000000
| A
| 16-bit Accumulator
|-
| 0x01000001
| X
| 16-bit X Index
|-
| 0x01000002
| Y
| 16-bit Y Index
|-
| 0x01000003
| S
| 16-bit Stack Pointer
|-
| 0x01000004
| D
| 16-bit Direct Page
|-
| 0x01000005
| PC
| Program Counter
|-
| 0x01000006
| P
| Processor Status
|-
| 0x01000007
| DBR
| Data Bank Register
|-
| 0x01000008
| PBR
| Program Bank Register
|-
| 0x01000009-0x01000FFF
| -
| Reserved
|-
| 0x01001000-0x0100FFFF
| -
| Reserved for future native mode extensions
|}
=== Family 0x02: Virtual machines ===
Virtual machines that run on MOS processors receive family identifier 0x02.
==== Type 0x00: SWEET-16 ====
SWEET-16 registers R0 through R15 occupy addresses 0x02000000 through 0x0200000F, with 0x02000010 through 0x0200FFFF reserved for expansion.
==== Type 0x01: Reserved for BBC VM ====
The range 0x02010000 through 0x0201FFFF is reserved for BBC VM register definitions.
==== Types 0x02-0xFF: Reserved ====
Reserved for additional virtual machine implementations.
=== Families 0x03-0x7F: Reserved ===
These family identifiers are reserved for future MOS-compatible architectures and processor variants.
== Family 0x03: SPC700 ==
The SPC700 (SNES sound processor) has a distinct architecture that differs from the 6502, requiring its own family allocation.
==== Type 0x00: Core registers ====
{| class="wikitable"
|-
! Address
! Register
! Description
|-
| 0x03000000
| A
| Accumulator
|-
| 0x03000001
| X
| X Index Register
|-
| 0x03000002
| Y
| Y Index Register
|-
| 0x03000003
| PSW
| Program Status Word (not compatible with 6502 P register)
|-
| 0x03000004
| SP
| Stack Pointer
|-
| 0x03000005
| PC
| Program Counter
|-
| 0x03000006
| YA
| Combined Y:A 16-bit register
|-
| 0x03000007-0x030000FF
| -
| Reserved
|}
== Chipset register assignments ==
Chipset registers occupy addresses 0x80000000 through 0xFFFFFFFF. The high bit set identifies these as chipset registers rather than processor registers.
<span id="platform-organization"></span>
=== Platform organization ===
{| class="wikitable"
|-
! Platform Range
! Category
! Description
|-
| 0x00-0x0F
| Generic
| Chips used across multiple systems
|-
| 0x10-0x7F
| System-Specific
| Platform-specific chips
|}
<span id="generic-chipset-platforms"></span>
=== Generic chipset platforms ===
<span id="platform-0x00-common-io-controllers"></span>
==== Platform 0x00: Common I/O controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x80010000-0x8001FFFF
| 6522 VIA
|-
| 0x02
| 0x80020000-0x8002FFFF
| 6551 ACIA
|-
| 0x03
| 0x80030000-0x8003FFFF
| 6526 CIA
|-
| 0x04
| 0x80040000-0x8004FFFF
| 6532 RIOT
|-
| 0x05-0xFF
| Reserved
| Future I/O controllers
|}
<span id="platform-0x01-common-sound-processors"></span>
==== Platform 0x01: Common sound processors ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x81010000-0x8101FFFF
| SID (6581/8580)
|-
| 0x02
| 0x81020000-0x8102FFFF
| POKEY
|-
| 0x03
| 0x81030000-0x8103FFFF
| AY-3-8910
|-
| 0x04
| 0x81040000-0x8104FFFF
| SN76489
|-
| 0x05-0xFF
| Reserved
| Future sound processors
|}
<span id="platform-0x02-common-video-controllers"></span>
==== Platform 0x02: Common video controllers ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x82010000-0x8201FFFF
| TMS9918
|-
| 0x02
| 0x82020000-0x8202FFFF
| MC6845 CRTC
|-
| 0x03-0xFF
| Reserved
| Future video controllers
|}
<span id="system-specific-chipset-platforms"></span>
=== System-specific chipset platforms ===
<span id="platform-0x10-commodore-systems"></span>
==== Platform 0x10: Commodore systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x90010000-0x9001FFFF
| VIC-II
|-
| 0x02
| 0x90020000-0x9002FFFF
| VIC (VIC-20)
|-
| 0x03
| 0x90030000-0x9003FFFF
| TED (Plus/4)
|-
| 0x04
| 0x90040000-0x9004FFFF
| VDC (C128)
|}
<span id="platform-0x11-apple-systems"></span>
==== Platform 0x11: Apple systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x91010000-0x9101FFFF
| Apple II 74LS graphics
|-
| 0x02
| 0x91020000-0x9102FFFF
| Disk II controller
|-
| 0x03
| 0x91030000-0x9103FFFF
| Apple IIgs custom chips
|}
<span id="platform-0x12-atari-systems"></span>
==== Platform 0x12: Atari systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x92010000-0x9201FFFF
| ANTIC
|-
| 0x02
| 0x92020000-0x9202FFFF
| GTIA
|-
| 0x03
| 0x92030000-0x9203FFFF
| FREDDIE
|}
<span id="platform-0x13-nintendo-systems"></span>
==== Platform 0x13: Nintendo systems ====
{| class="wikitable"
|-
! Chipset
! Address Range
! Device
|-
| 0x01
| 0x93010000-0x9301FFFF
| PPU
|-
| 0x02
| 0x93020000-0x9302FFFF
| APU
|-
| 0x03
| 0x93030000-0x9303FFFF
| NROM (No mapper)
|-
| 0x04
| 0x93040000-0x9304FFFF
| MMC1 (SxROM)
|-
| 0x05
| 0x93050000-0x9305FFFF
| UNROM
|-
| 0x06
| 0x93060000-0x9306FFFF
| CNROM
|-
| 0x07
| 0x93070000-0x9307FFFF
| MMC3 (TxROM)
|-
| 0x08
| 0x93080000-0x9308FFFF
| AOROM
|-
| 0x09
| 0x93090000-0x9309FFFF
| MMC5 (ExROM)
|-
| 0x0A
| 0x930A0000-0x930AFFFF
| MMC2 (PxROM)
|-
| 0x0B
| 0x930B0000-0x930BFFFF
| MMC4 (FxROM)
|-
| 0x0C
| 0x930C0000-0x930CFFFF
| ColorDreams
|-
| 0x0D
| 0x930D0000-0x930DFFFF
| CPROM
|-
| 0x0E
| 0x930E0000-0x930EFFFF
| GXROM
|-
| 0x0F
| 0x930F0000-0x930FFFFF
| GTROM
|-
| 0x10
| 0x93100000-0x9310FFFF
| UNROM-512
|-
| 0x11
| 0x93110000-0x9311FFFF
| Action53
|-
| 0x12
| 0x93120000-0x9312FFFF
| AxROM
|-
| 0x13
| 0x93130000-0x9313FFFF
| BNROM
|-
| 0x14
| 0x93140000-0x9314FFFF
| Camerica
|-
| 0x15
| 0x93150000-0x9315FFFF
| FME-7 (Sunsoft 5B)
|-
| 0x16
| 0x93160000-0x9316FFFF
| VRC2
|-
| 0x17
| 0x93170000-0x9317FFFF
| VRC4
|-
| 0x18
| 0x93180000-0x9318FFFF
| VRC6
|-
| 0x19
| 0x93190000-0x9319FFFF
| VRC7
|-
| 0x1A
| 0x931A0000-0x931AFFFF
| Namco 163
|-
| 0x1B
| 0x931B0000-0x931BFFFF
| Namco 106/109/118/119
|-
| 0x1C
| 0x931C0000-0x931CFFFF
| TQROM (MMC3 + CHR-RAM)
|-
| 0x1D-0xFF
| Reserved
| Future mapper variants
|}
<span id="extended-register-concepts"></span>
== Extended register concepts ==
<span id="banking-support"></span>
=== Banking support ===
Memory banking schemes receive their own type identifiers within a family's allocation. This allows the register allocator to track variables across bank switches and optimize banking operations as register allocation decisions. Each bankable memory location can be treated as a distinct register for allocation purposes.
<span id="lsb-subregisters"></span>
=== LSB subregisters ===
LLVM-MOS uses LSB subregisters to store boolean values in 8-bit registers. Each 8-bit imaginary register (RC, RL, RQ types) has an associated 1-bit LSB subregister. These subregisters receive their own DWARF numbers to support debugging and register allocation. The LSB subregisters are allocated in separate ranges from their parent registers.
<span id="implementation-notes"></span>
== Implementation notes ==
The DWARF format uses ULEB128 encoding for register numbers. Programs using only registers 0-5 require minimal encoding space. Programs using extended registers pay encoding costs proportional to the register numbers used.
The structured format provides an allocation strategy for this specification. The meaning encoded in the bit patterns is for allocation management.
<span id="rationale"></span>
== Rationale ==
The MOS processor family has unique characteristics that require careful consideration in DWARF register numbering. Unlike modern processors with large, uniform register files, MOS processors blur the distinction between registers and memory. Zero page locations function as pseudo-registers, memory-mapped I/O addresses control hardware state, and banking schemes multiply the effective register space. Virtual machines like SWEET-16 add their own register sets on top of the base processor.
Previous architectures have encountered problems with their DWARF register numbering schemes. The x86 architecture assigned the same DWARF numbers to both XMM and YMM registers, creating ambiguity that persists in debugging tools. ARM's initial VFP and FPA registers overlapped in the range 16-47, forcing a later reallocation to 64-95 and complicating tool compatibility. Many architectures exhausted their initial register number allocations when new extensions emerged.
There is little architectural penalty for having multiple DWARF numbers referring to the same registers, or to sub-regions of other registers. However, reusing DWARF numbers to have different contextual meanings, makes debugger developers very unhappy for very long periods of time.
<span id="references"></span>
== References ==
* [http://archive.6502.org/books/mcs6500_family_programming_manual.pdf MOS 6502 Programming Manual]
* [http://archive.6502.org/datasheets/wdc_65816_programming_manual.pdf Programming the 65816]
* [https://github.com/llvm-mos/llvm-mos LLVM-MOS Repository]
* [https://llvm-mos.org/wiki/Calling_convention LLVM-MOS Calling Convention]
* [https://docs.mamedev.org/debugger/index.html MAME Debugger Documentation]
* [https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc RISC-V DWARF Register Numbers]
* [https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst ARM DWARF for AArch64]
* [https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf x86-64 System V ABI]
5c3f343eb784b4e31a5e9d74adc255aa52045e02
DWARF overview
0
77
582
2025-09-28T08:07:23Z
Jbyrd
1
Initial implementation
wikitext
text/x-wiki
=== What's DWARF? Why is it relevant to llvm-mos? ===
DWARF is a debugging data format that connects your source code to machine code. When you compile a C program with `-g`, the compiler embeds DWARF information alongside your executable. This data tells debuggers like GDB which source line corresponds to each machine instruction, where variables are stored, and how to map register contents back to your program's symbols.
Modern debuggers rely entirely on DWARF to provide source-level debugging. Without it, you're limited to assembly-level debugging with raw memory addresses and register dumps.
=== Why this specification is needed ===
The 6502 family presents unique challenges that modern DWARF wasn't designed for. Unlike contemporary processors with large, uniform register files, 6502 systems blur the line between registers and memory. Zero page locations function as compiler-allocated pseudo-registers. Memory-mapped I/O registers control video chips, sound processors, and other peripherals. Banking schemes multiply the effective address space.
Different retro development tools have historically assigned their own register numbers, creating incompatibility. An emulator might number the accumulator as register 0, while a compiler uses register 3 for the same hardware. This makes it impossible to share debugging information between tools or use multiple debuggers on the same program.
=== What this specification provides ===
This specification establishes a standardized numbering scheme for all registers in 6502-family systems. It defines numbers for CPU registers, status flags, compiler-generated imaginary registers, and chipset registers from video controllers to memory mappers. The system prevents numbering conflicts while remaining compatible with existing tools.
=== When programmers need to know this ===
You may need to understand this specification if you're developing retro programming tools, implementing DWARF support in emulators, working with compilers like LLVM-MOS that use advanced register allocation, or building debuggers for 6502 systems. Most casual retro programmers can remain unaware of these details—the specification works behind the scenes to ensure their debugging tools are compatible with each other.
The full DWARF specification is at [[DWARF specification]].
e69b3f5ac5e54d6aaf7cfc1a4a3b82ac92cab175